Поддерживаемый базой данных REST API со слугой?

Я сталкиваюсь с проблемой при настройке простого доказательства концепции API. Это мой тип данных User и мой тип API:

data User = User { id :: Int, first_name :: String, last_name :: String } deriving (Eq, Show, Generic)
instance FromRow User
instance ToRow User
$(deriveJSON defaultOptions ''User)

type API = "users" :> ReqBody '[JSON] User :> Post '[JSON] User

Для этого используется метод-обработчик postgresql-simple:

create u = liftIO $ head <$> returning connection "insert into users (first_name, last_name) values (?,?) returning id" [(first_name u, last_name u)]

Код Boilerplate, такой как подключение к БД и способ маршрутизации, был исключен. Проблема в том, что если я сделаю POST-запрос, я хочу создать нового пользователя, поэтому я бы поставил JSON:

{ "first_name": "jeff", "last_name": "lebowski" }

но тогда моя программа не работает во время выполнения с

Error in $: When parsing the record User of type Lib.User the key id was not present. 

Это имеет смысл, потому что API указал User, у которого есть поле id. Но я не хочу передавать фиктивный идентификатор в запросе (поскольку он присваивается postgres последовательно), потому что это брутто. Я также не могу переместить поле id из типа данных User, потому что тогда postgres-simple завершается неудачно из-за несоответствия базы данных модели при выполнении запроса GET к другой конечной точке (что делает очевидную вещь: получение по id. Не включено выше). Что мне здесь делать? Написать собственный экземпляр FromJson? Я уже пытался установить для параметра Data.Aeson.TH флаг omitNothingFields значение True и установить для поля id значение Maybe Int, но это тоже не сработало. Любой совет будет принят во внимание.

1 ответ

Во-первых, вы должны понимать, что пользователь и строка в таблице, соответствующей этому пользователю, - это две разные вещи.

У строки есть идентификатор, а у пользователя - нет. Например, вы можете представить себе сравнение двух пользователей без учета идентификаторов или того факта, что они сохранены или нет.

После того, как вы убедитесь, вам придется объяснить это системе типов, или вам придется иметь дело с полями Maybe, которые, я думаю, здесь не являются решением.

Некоторые люди говорили о Template Haskell, я думаю, что это было бы излишним, и вам нужно сначала решить проблему.

Что вы можете сделать, это использовать тип данных для представления сохраненной строки в вашей базе данных. Давайте назовем это Entity.

newtype PrimaryKey = PrimaryKey Int

data Entity b = Entity PrimaryKey b

Тогда функция сохранения строки User в вашей базе данных может занять user в качестве параметра и вернуть PrimaryKey (В вашей базе монада, конечно). Другие функции чтения из базы данных будут возвращать что-то с помощью Entity User

Ваши объявления полей не дублируются, так как вы повторно используете тип User в качестве параметра.

Вам придется адаптировать FromRow/ToRow и FromJSON/ToJSON соответственно.

Другие вопросы по тегам