Question

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?

Was it helpful?

Solution

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
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top