Search code examples
haskelltemplate-haskellscotty

Dynamically add routes at compile time in Scotty


Is it possible to add routes dynamically at compile time through a config file by using Template Haskell or any other way.

Scotty has a function addRoute but I want to use it dynamically.

Example

import qualified Data.Text.Lazy as LTB

sampleRoutes :: [(String, LTB.Text)]
sampleRoutes = [("hello", LTB.pack "hello"), ("world", LTB.pack "world")]

I want to iterate over the sampleRoutes array and define routes and responses at compile time.

import Web.Scotty

main = scotty 3000 $ do
  middleware logStdoutDev
  someFunc sampleRoutes

Solution

  • OK, so given the above list, I am assuming you are looking to something that is equivalent to writing the following by hand:

    {-! LANGUAGE OverloadedStrings #-}
    import Web.Scotty
    import Data.String
    
    main = scotty 3000 $ do
      middleware logStdoutDev
      get (fromString $ '/' : "hello") (text "hello")
      get (fromString $ '/' : "world") (text "world")
    

    The good news is, there's nothing in there that would need any TH magic!

    Remember, addroute / get are just regular functions that return a ScottyM () value. If I have

    r1 = get (fromString $ '/' : "hello") (text "hello")
    r2 = get (fromString $ '/' : "world") (text "world")
    

    then the preceding main function is exactly equivalent to

    main = do
      middleware logStdoutDev
      r1
      r2
    

    This, and the common structure of r1 and r2 suggests the following solution:

    import Control.Monad (forM_)
    
    main = do
      middleware logStdoutDev
      forM_ sampleRoutes $ \(name, response) -> 
        get (fromString $ '/':name) (text response)