Adding simple seed data in Yesod

Posted on Dec 3, 2016 by Alexej Bondarenko

There are several reasons to create seed data for your application. For instance, it could be for running tests or just create example data you can work with during the development of your application. In this blog post, I would like to show how this can be achieved in easily in Yesod.

First of all, let's create a scenario. For me, a common use case it to have different sample accounts (with different access rights) for development so I can use them during my development process. To stick as close as possible to the scaffolded Yesod project, I'll just use the default User model:

  email Text
  password Text Maybe -- Password may not be set yet
  verified Bool
  UniqueUser email
  deriving Typeable
  deriving Show

As you can see, nothing special is defined in this model. We just have a simple email/password combination and a verification flag.

Next, we need to define our seed implementation. Here for we just create a new folder seed and place a new file UserSeed.hs inside this folder with the following content:

{-# LANGUAGE NoImplicitPrelude, OverloadedStrings #-}
import Import
import Model
import Control.Monad.Logger (runStderrLoggingT)
import Database.Persist.Postgresql (pgConnStr, withPostgresqlConn, runSqlConn, rawExecute)

main :: IO ()
main = do
  settings <- loadYamlSettingsArgs [configSettingsYmlValue] useEnv
  let conn = (pgConnStr $ appDatabaseConf settings)
  runStderrLoggingT . withPostgresqlConn conn $ runSqlConn $ do
    runMigration migrateAll
    insert_ $ User "" (Just "testpassword") True

It's important to realize that we define the main function to be able to execute our seed script. The rest is very straight forward since we just use the default Yesod Import and migration. With the help of our Logger we will be able to see which SQL is executed.

Within the do block, we can execute all possible SQL queries (inserts, deletes, ...) which are provided by Persistent.

As the last step, we need to tell stack/cabal how this script can be executed. To achieve this we modify the projects .cabal file and add a new executable on top of the already existent executable definition like this:

executable      ersocon-tutorial-seed-user
    if flag(library-only)
        Buildable: False

    main-is:           UserSeed.hs
    hs-source-dirs:    seed
    build-depends:     base
                     , ersocon-tutorial-app
                     , monad-logger
                     , persistent
                     , persistent-postgresql

We gave the executable the name ersocon-tutorial-seed-user and defined the source dir to be seed with the main executable function in UserSeed.hs. Furthermore, don't forget to define persistent and logger dependencies. That's it!

To execute our defined seed script we can install stack-run which has a handy interactive mode. After installation we just run the following command inside our application and select the seed executable by using the arrow keys:

stack-run -i

If everything is correct, you should see a debug output for a single insert query. Please be aware that we use the default configuration here, so it is not the test database that you modify here. If you would like to populate your test database instead you just need to load the correct configuration.

Hint: Just in case you may run into linking issues. Make sure you don't run yesod devel at same time you execute the script.

I hope that this little tutorial helped to find out how to easily define seed data inside your Yesod application. If you have any questions or comments, just use the are below.