Environment variables for configuration

There’s a recent move, perhaps most prominently advocated by the twelve-factor app, to store all app configuration in environment variables, instead of using config files or hard-coding them into the application source code (you don’t do that, right?).

Yesod’s scaffolding comes built in with some support for this, most specifically for respecting the YESOD_APPROOT environment variable to indicate how URLs should be generated, the YESOD_PORT environment variable for which port to listen for requests on, and database connection settings. (Incidentally, this ties in nicely with the Keter deployment manager.)

The technique for doing this is quite easy: just do the environment variable lookup in your main function. This example demonstrates this technique, along with the slightly special handling necessary for setting the application root.

  1. {-# LANGUAGE OverloadedStrings #-}
  2. {-# LANGUAGE QuasiQuotes #-}
  3. {-# LANGUAGE RecordWildCards #-}
  4. {-# LANGUAGE TemplateHaskell #-}
  5. {-# LANGUAGE TypeFamilies #-}
  6. import Data.Text (Text, pack)
  7. import System.Environment
  8. import Yesod
  9. data App = App
  10. { myApproot :: Text
  11. , welcomeMessage :: Text
  12. }
  13. mkYesod "App" [parseRoutes|
  14. / HomeR GET
  15. |]
  16. instance Yesod App where
  17. approot = ApprootMaster myApproot
  18. getHomeR :: Handler Html
  19. getHomeR = defaultLayout $ do
  20. App {..} <- getYesod
  21. setTitle "Environment variables"
  22. [whamlet|
  23. <p>Here's the welcome message: #{welcomeMessage}
  24. <p>
  25. <a href=@{HomeR}>And a link to: @{HomeR}
  26. |]
  27. main :: IO ()
  28. main = do
  29. myApproot <- fmap pack $ getEnv "YESOD_APPROOT"
  30. welcomeMessage <- fmap pack $ getEnv "WELCOME_MESSAGE"
  31. warp 3000 App {..}

The only tricky things here are:

  1. You need to convert the String value returned by getEnv into a Text by using pack.

  2. We use the ApprootMaster constructor for approot, which says “apply this function to the foundation datatype to get the actual application root.”