Hi all,

sorry for the long break. It started with me getting the last DLC for The Witcher 3 – Blood & Wine (check it out if you didn’t play it yet), and then loads of stuff happened. Anyway, I’m back and I’m going to do my best to make up for the delay. Let’s start with parametrized routes, like /project/10, eat/200 etc. We’re going to add support for such routes in our project management app for each of the projects, indexed by some unique ID (artificially given).

Yesod handles routes in a relatively simple way – whole route is splitted by ‘/’ (forward slash) and a list is generated – for example, /project/2 will translate to ["project", "2"]. Don’t worry about trailing slashes or double-slashes, Yesod has you covered – routes are translated to a canonical form and users are redirected to them. In Yesod you specify a route parameter by it’s expected type name preceeded by # (if the value is between two forward slashes) or * (in case of more complex type).

Hence adding a new route is simple – just add

/project/#Int ProjectR GET

line to config/routes and we’re done. Well, not entirely – we still need to add proper handler, and that’s what we’re going to do now.
To do it properly, create a Project.hs file and add it to all required places (you can check which places are required in one of earlier posts). It’s content should be simple:

module Handler.Project where
import Import

import Yesod.Bootstrap()

getProjectR :: Int -> Handler Html
getProjectR projectId = defaultLayout $ dodo
  setTitle $ ("Project " ++ (toHtml projectId))
$(widgetFile "project")

This also requires a projects.hamlet file, equally simple:

<div>
  <h1 .page-header> Project: #{show projectId}

aaaand it’s working – check it out yourself! Here’s a little bonus – on a request for a resource that doesn’t match the type signature (for example, project/x) Yesod will automatically return 404: Not Found without even bothering you. Current 404 page isn’t nice, but it’s customizeable, obviously (we’re gonna deal with it in the future).

You might’ve wondered what will happen if you create two routes matching the same pattern, for example /project/#Int and /project/#Text (overlapping part is everything that is parseable by /project/#Int). Try it out yourself! Apprently, quite a nice exception is thrown:

Overlapping routes:
("ProjectR","ProjectR")

but what if you actually want routes to overlap, and simply choose “numeric handler when argument is numeric, and textual when it isn’t”? Good news is that you don’t need to push parsing logic into the handler – you simply need to change the route definition from /project/#Int and /project/#Text to disable overlap checking – but only for these routes. If you do that, be aware – first matching route wins, so be careful with the order!

Next episode – linking with typesafe routes – coming still this week. Stay tuned!