Building a Blog in Haskell with Yesod–Returning JSON
Riccardo Odone
Posted on August 12, 2019
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 ]
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"
}
]
}
Get the latest content via email from me personally. Reply with your thoughts. Let's learn from each other. Subscribe to my PinkLetter!
Posted on August 12, 2019
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.