RealWorld example with Haskell Yesod

August 14, 2018

This is an overview of an example implementation of the RealWorld backend built using the powerful Yesod framework written in Haskell.

This project is essentially a RESTful API to a database and even though Yesod can do much more than that, you still get a lot of nice things that help along the way. For example, Yesod comes with some helper commands for common tasks (e.g. you can add a new handler using yesod add-handler) and you can start a new project using one of the stack templates. I’ve started this project with stack new yesod-sqlite which scaffolded application with SQLite database connection, whose schema is created from persistent entity file. The scaffolded project is setup with:

If you want to learn more about Yesod, you can read the excellent Developing Web Applications with Haskell and Yesod for free. Some other dependencies used in this project are:

Project structure

app
├── app
├── package.yaml
│   ├── main.hs                     "The main entry point for production build."
│   ├── devel.hs                    "The main for development server."
│   └── DevelMain.hs                "The main for running inside GHCi."
├── config
│   ├── models                      "Persistent entity file with the database schema declaration."
│   ├── routes                      "Routes declaration for our RESTful API."
│   ├── settings.yml                "App runtime settings."
│   └── test-settings.yml           "Settings overrides for tests."
├── src
│   ├── Application.hs              "Loads the settings to initalize the app with a HTTP manager,"
│   │                               "logger and database connection pool."
│   │
│   ├── Foundation.hs               "Declares the foundation 'App' datatype which is accessible"
│   │                               "from every handler, type for routes loaded from `config/routes`"
│   │                               "and authentication requirements for each route."
│   │
│   ├── Settings.hs                 "Declares and loads the app settings, which can be loaded from"
│   │                               "various sources."
│   │
│   ├── Import.hs                   "Commonly used module imports grouped for convenience."
│   ├── Model.hs                    "Generates types for models loaded from `config/models`."
│   ├── Database.hs                 "Database queries."
│   ├── Pagination.hs
│   ├── Handler                     "Handlers for the RESTful API."
│   │   ├── Articles.hs
│   │   ├── Profiles.hs
│   │   └── User.hs
│   ├── Database                    "Helpers for dealing with database."
│   │   └── Persist
│   │       ├── Extended.hs
│   │       └── Types
│   │           ├── Email           "Persistent field type for email, which is"
│   │           │   └── Internal.hs "stored in a case-insensitive string."
│   │           ├── Email.hs
│   │           ├── Password        "Persistent field type for password,"
│   │           │   └── Internal.hs "stored as a hash."
│   │           └── Password.hs
│   ├── Auth                        "Authentication using JWT token."
│   │   └── JWT.hs
│   └── Web                         "User input validation."
│       └── Forma
│           └── Extended.hs
├── test                            "The specs for user and profiles handlers."
│   ├── Handler
│   │   ├── ProfilesSpec.hs
│   │   └── UserSpec.hs
│   ├── Spec.hs
│   └── TestImport.hs

Some notes

The handlers are implemented according to the API spec:

comments powered by Disqus