Visitor counter

Remember back in the good ol’ days of the internet, where no website was complete without a little “you are visitor number 32” thingy? Ahh, those were the good times! Let’s recreate that wonderful experience in Yesod!

Now, if we wanted to do this properly, we’d store this information in some kind of persistent storage layer, like a database, so that the information could be shared across multiple horizontally-scaled web servers, and so that the information would survive an app restart.

But our goal here isn’t to demonstrate good practice (after all, if it was about good practice, I wouldn’t be demonstrating a visitor counter, right?). Instead, this is meant to provide a simple example of sharing some state among multiple handlers. A real-world use case would be caching information across requests. Just remember that when you use the technique we’ll be showing, you need to be careful about multiple app servers and app restarts.

The technique is simple: we create a new field in the foundation datatype for a mutable reference to some data, and then access it in each handler. The technique is so simple, it’s worth just diving into the code:

  1. {-# LANGUAGE OverloadedStrings #-}
  2. {-# LANGUAGE QuasiQuotes #-}
  3. {-# LANGUAGE TemplateHaskell #-}
  4. {-# LANGUAGE TypeFamilies #-}
  5. import Data.IORef
  6. import Yesod
  7. data App = App
  8. { visitors :: IORef Int
  9. }
  10. mkYesod "App" [parseRoutes|
  11. / HomeR GET
  12. |]
  13. instance Yesod App
  14. getHomeR :: Handler Html
  15. getHomeR = do
  16. visitorsRef <- fmap visitors getYesod
  17. visitors <-
  18. liftIO $ atomicModifyIORef visitorsRef $ \i ->
  19. (i + 1, i + 1)
  20. defaultLayout
  21. [whamlet|
  22. <p>Welcome, you are visitor number #{visitors}.
  23. |]
  24. main :: IO ()
  25. main = do
  26. visitorsRef <- newIORef 0
  27. warp 3000 App
  28. { visitors = visitorsRef
  29. }

I used IORef here, since we didn’t need anything more than it provided, but you’re free to use MVars or TVars as well. In fact, a good exercise for the reader is to modify the above program to store the visitor count in a TVar instead.