Scotty api with mysql simple in haskell

325 Views Asked by At

I am following this tutorial which uses scotty with persistent to create a simple API .

However, I am trying to create a simple api with scotty and mysql simple library.

Now I am stuck at one point in code .

In the below code I am not able to convert getUser function to type "ActionT Error ConfigM" because of which my code is failing.

Can anyone help me with understanding how I can convert getUser function to achieve needed type signature?

Code

type Error = Text
type Action = ActionT Error ConfigM ()

config :: Config
config = Config
    { environment = Development 
     ,db1Conn = connect connectionInfo
    }

main :: IO ()
main = do
  runApplication config

runApplication :: Config -> IO ()
runApplication c = do
  o <- getOptions (environment c)
  let r m = runReaderT (runConfigM m) c
  scottyOptsT o r application

application :: ScottyT Error ConfigM ()
application = do
  e <- lift (asks environment)
  get "/user" getTasksA

getTasksA :: Action
getTasksA = do
    u <- getUser
    json u 

getUser :: IO User
getUser = do
  e <- asks environment
  conn <- db1Conn config 
  [user]<- query_ conn "select login as userId, email as userEmail from member limit 1"
  return user 

Error

• Couldn't match type ‘IO’ with ‘ActionT Error ConfigM’
      Expected type: ActionT Error ConfigM User
        Actual type: IO User
    • In a stmt of a 'do' block: u <- getUser
      In the expression:
        do { u <- getUser;
             json u }
      In an equation for ‘getTasksA’:
          getTasksA
            = do { u <- getUser;
                   json u }
1

There are 1 best solutions below

1
On

You left out plenty of code (imports and pragmas and the definitions of User, please include that next time - see MCVE.

But now to your question:

I would change the Action type to the following

type Action a = ActionT Error ConfigM a

then getTasksA has the following type signature

getTasksA :: Action ()
getTasksA = do
    u <- getUser
    json u 

(alternatively you can write this as getTasksA = getUser >>= json)

and getUser

getUser :: Action User
getUser = do
  e <- asks environment
  conn <- db1Conn config 
  [user] <- liftIO $ query_ conn "select login as userId, ..."
  return user

A few remarks

  • [user] <- liftIO $ query .. is a bad idea - if no user is found this crashes your application - try to write total functions and pattern matches. Better return a Maybe User.

    getUser :: Action (Maybe User)
    getUser = do
      e <- asks environment
      conn <- db1Conn config 
      fmap listToMaybe . liftIO $ query_ conn "select login as userId, ..."
    
  • if you can wrap your head around it, rather use persistent than writing your SQL queries by hand - this is quite error prone, especially when refactoring, just imagine renaming userId to userID.

  • you ask several times for the environment but then don't use it. Compile with -Wall or even -Werror to get a warning or even elevate warnings to compile errors (which is a good idea for production settings.