Building a Blog in Haskell with Yesod–Returning JSON

Posted on August 12, 2019 by Riccardo

Building a Blog in Haskell with Yesod (Series)

Building a Blog in Haskell with Yesod–The Basic StructureBuilding a Blog in Haskell with Yesod–Using a DatabaseBuilding a Blog in Haskell with Yesod–AuthenticationBuilding a Blog in Haskell with Yesod–AuthorizationBuilding a Blog in Haskell with Yesod–Returning JSONBuilding a Blog in Haskell with Yesod–JSON APIBuilding a Blog in Haskell with Yesod–Giving Back

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 ]

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
      }
    ]
  ]
}

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
        ]
      ]

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"
    }
  ]
}

PinkLetter

It's one of the selected few I follow every week – Mateusz

Tired of RELEARNING webdev stuff?

  • A 100+ page book with the best links I curated over the years
  • An email once a week full of timeless software wisdom
  • Your recommended weekly dose of pink
  • Try before you buy? Check the archives.