带有服务程序的数据库支持REST API



我在设置一个简单的概念验证服务API时遇到了一个问题。这是我的用户数据类型和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,简单如下:

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指定了一个具有id字段的User。但我不想在请求中传递虚假的id(因为它们是由postgres按顺序分配的(,因为这太恶心了。我也不能将id字段从User数据类型中移出,因为当向不同的端点发出GET请求时,postgres simple会因为模型数据库不匹配而失败(这会做一件显而易见的事情:通过id获取。以上不包括(。我在这里做什么?写一个自定义的FromJson实例?我已经尝试设置数据。伊森。TH选项标志省略NothingFields为True,并将id字段设置为Maybe Int,但这也不起作用。如有任何建议,我们将不胜感激。

首先,您需要了解一个User和表中与该User对应的行是两种不同的东西。

行有id,用户没有。例如,您可以想象在不处理id的情况下比较两个用户,或者不处理它们是否被保存的事实。

一旦被说服,你将不得不向类型系统解释这一点,否则你将不得不处理Maybe字段,我认为这不是这里的解决方案。

有些人谈到了Template Haskell,我认为这在这里太夸张了,你需要首先解决问题。

您可以使用数据类型来表示数据库中保存的行。让我们称之为实体。

newtype PrimaryKey = PrimaryKey Int
data Entity b = Entity PrimaryKey b

然后,在数据库中保存User行的函数可以将user作为参数,并返回PrimaryKey(当然,在您的数据库monad中(。从数据库读取的其他函数将使用Entity User 返回一些内容

字段声明不会重复,因为您将User类型重新用作参数。

您必须相应地调整FromRow/ToRow和FromJSON/ToJSON。

最新更新