DEV Community

Cover image for Building a Blog in Haskell with Yesod–Returning JSON
Riccardo Odone
Riccardo Odone

Posted on • Edited on • Originally published at odone.io

Building a Blog in Haskell with Yesod–Returning JSON

You can keep reading here or jump to my blog to get the full experience, including the wonderful pink, blue and white palette.


This is a series about Yesod: a Haskell web framework that follows a similar philosophy to Rails. In fact, it is strongly opinionated and provides a lot of functionality out of the box.

A good read about Yesod is available online for free: Developing web applications with Haskell and Yesod. That's why this series will be a commentary of the commits from a repo we will use to develop a super simple blog.

In other words, this won't be good material to learn how to use Yesod. However, it will hopefully give an overview of how the framework works.

Gimme JSON!

The plan for this post was to transform the entire blog into an API. Unfortunately, the compiler got in the middle. Therefore, this is just the first step towards that goal.

In particular, here we are going to see how to return the list of posts as JSON instead of as HTML.

Commit 53c06240c8b41438f35475284588e67950ff8800 is the initial attempt.

By adding json to the Post and User model, we get for free a ToJSON instance. Then, we can use it in the handler with

return $ object [ "posts" .= allPosts ]
Enter fullscreen mode Exit fullscreen mode

Notice that allPosts is a list of tuples. In particular, (post, user). That's why the final JSON looks like the following:

{
  "posts":[
    [
      {
        "text":"Luigi",
        "userId":3,
        "id":5,
        "title":"I am"
      },
      {
        "password":null,
        "ident":"luigi",
        "id":3
      }
    ],
    [
      {
        "text":"333",
        "userId":2,
        "id":4,
        "title":"333"
      },
      {
        "password":null,
        "ident":"mario",
        "id":2
      }
    ],
    [
      {
        "text":"text",
        "userId":2,
        "id":3,
        "title":"title"
      },
      {
        "password":null,
        "ident":"mario",
        "id":2
      }
    ],
    [
      {
        "text":"text",
        "userId":1,
        "id":2,
        "title":"title"
      },
      {
        "password":null,
        "ident":"riccardo",
        "id":1
      }
    ],
    [
      {
        "text":"1",
        "userId":1,
        "id":1,
        "title":"1"
      },
      {
        "password":null,
        "ident":"riccardo",
        "id":1
      }
    ]
  ]
}
Enter fullscreen mode Exit fullscreen mode

Custom JSON

It's cool to have the ToJSON instances generated for free. Unfortunately, that means we don't have any control over the content of the JSON.

Commit 70e71484dd979fe43adf6295f4f6110e974a3214 fixes that by wrapping the (post, user) tuple into a new datatype with its own ToJSON instance:

instance ToJSON PostData where
  toJSON (PostData (postEntity, userEntity)) =
    let
      post = entityVal postEntity
      postId = entityKey postEntity
      user = entityVal userEntity
      userId = entityKey userEntity
    in
    object
      [ "id" .= postId
      , "title" .= postTitle post
      , "text" .= postText post
      , "user" .= object
        [ "id" .= userId
        , "username" .= userIdent user
        ]
      ]
Enter fullscreen mode Exit fullscreen mode

That means our JSON now looks like this

{
  "posts":[
    {
      "text":"Luigi",
      "user":{
        "username":"luigi",
        "id":3
      },
      "id":5,
      "title":"I am"
    },
    {
      "text":"333",
      "user":{
        "username":"mario",
        "id":2
      },
      "id":4,
      "title":"333"
    },
    {
      "text":"text",
      "user":{
        "username":"mario",
        "id":2
      },
      "id":3,
      "title":"title"
    },
    {
      "text":"text",
      "user":{
        "username":"riccardo",
        "id":1
      },
      "id":2,
      "title":"title"
    },
    {
      "text":"1",
      "user":{
        "username":"riccardo",
        "id":1
      },
      "id":1,
      "title":"1"
    }
  ]
}
Enter fullscreen mode Exit fullscreen mode

Get the latest content via email from me personally. Reply with your thoughts. Let's learn from each other. Subscribe to my PinkLetter!

Top comments (0)