I am new to Haskell and am trying to write a Scotty web API with Persistent as the ORM. I am having problems with the following code:
share [mkPersist sqlSettings, mkMigrate "migrateAll"] [persistLowerCase|
Account json
name String
description T.Text
deriving Show
...
|]
resource :: (PersistEntity a, PersistEntityBackend a ~ SqlBackend, ToJSON a, ToJSON (Entity a), FromJSON a) => String -> (Key a -> Key a) -> [SelectOpt a] -> Pool Connection -> S.ScottyM ()
resource url keyConv selectOpts pool = do
S.get (S.capture $ pluralize url) $ do
objects <- runDb pool $ selectList [] selectOpts
S.json $ map entityIdToJSON objects
S.get (S.capture $ url ++ "/:id") $ do
idParam <- S.param "id"
found <- runDb pool . getJust . keyConv . key $ idParam
S.json $ found
...
main :: IO ()
main = withSqlitePool "bla.db" 2 $ \pool -> do
S.scotty 3000 $ do
resource "/account" accountKey [Asc AccountId] pool
...
The resource
function defines Scotty web routes for Persistent model objects I want to expose.
The first route defined by resource
retrieves the whole collection. E.g. a HTTP GET
to /accounts
returns all accounts.
The second route defined by resource
retrieves a specific entity by id. E.g. a HTTP GET
to /account/1
returns the account with id 1.
The functions work as they are, but I don't like how the default json serialization doesn't include the id, which is why I used entityIdToJSON
in the get all route.
However, when trying to include entityIdToJSON
in the get by id route, on the line with S.json $ found
, I get the following type error:
Main.hs:61:31-35: Could not deduce (a ~ Entity e0) …
from the context (PersistEntity a,
PersistEntityBackend a ~ SqlBackend,
ToJSON a,
ToJSON (Entity a),
FromJSON a)
bound by the type signature for
resource :: (PersistEntity a, PersistEntityBackend a ~ SqlBackend,
ToJSON a, ToJSON (Entity a), FromJSON a) =>
String
-> (Key a -> Key a)
-> [SelectOpt a]
-> Pool Connection
-> S.ScottyM ()
at /home/poida/src/moneymoney-api-scotty/Main.hs:48:13-189
`a' is a rigid type variable bound by
the type signature for
resource :: (PersistEntity a, PersistEntityBackend a ~ SqlBackend,
ToJSON a, ToJSON (Entity a), FromJSON a) =>
String
-> (Key a -> Key a)
-> [SelectOpt a]
-> Pool Connection
-> S.ScottyM ()
at /home/poida/src/moneymoney-api-scotty/Main.hs:48:13
In the second argument of `($)', namely `found'
In the second argument of `($)', namely `entityIdToJSON $ found'
In a stmt of a 'do' block: S.json $ entityIdToJSON $ found
Compilation failed.
I've tried adding the type constraint Entity a
to resource, that made things worse. I've also tried digging around in the documentation on hackage but I don't quite understand all that I'm reading. The docs seem to indicate that a Key is the Entity along with its key.
tl;dr - My problem is trying to use the result of Persistent's getJust function, a Key, with Persistent's entityIdToJSON function which expects an Entity.
Any ideas?
I think you'll need to combine your key with found
to produce an Entity
.
Store the key you pass to getJust
(break the line apart to store keyConv . key $ idParam
in a let
), then make an Entity
from the stored key and the found
.
Something like (untested):
S.get (S.capture $ url ++ "/:id") $ do
idParam <- S.param "id"
let k = keyConv . key $ idParam
found <- runDb pool . getJust $ k
S.json $ Entity k found