I’m pleased to announce the serversession family of packages.
HTTP is a stateless protocol. Cookies are used to create sessions out of otherwise independent requests made by the browser to the server. There are many ways of managing sessions via cookies, but they can be mostly separated into two big camps:
- Client-side sessions
- The cookie data contains the session data. For example, it could contain a shopper’s login and cart contents.
- Server-side sessions
- The cookie data contains a session identifier, and the session data is kept on a database indexed by the session identifiers.
However, there are many reasons why one may want to favor server-side sessions over client-side ones:
- Saving arbitrary amounts of session data without being constrained by cookie size limits. Even if your data fits on a cookie, you may want to spare having to bounce all that data around on every request and response.
- Securely invalidating a session. User logout with client-side sessions erases the cookie from the user’s browser but doesn’t invalidate its data. The old cookie is still valid until its expiration. Server-side sessions, however, can be securely invalidate by simply erasing them from your session database. This can be a critical feature depending on your needs.
The biggest disadvantage of server-side sessions is that they need more server-side resources. Not only it needs space for sessions storage, but it also incurs the overhead of zero to two DB transactions per HTTP request.
One solution isn’t inherently better than the other. It all depends on your use case. And if your use case needs server-side sessions, this is your lucky day.
The serversession packages
I’d like to fill the gap that currently exists on the Haskell ecosystem with respect to server-side session support. Preferably once and for all. That’s why there are many serversession packages.
The main one, aptly called serversession, contains the core logic about server-side sessions. It’s abstracted over a backend which will store the session’s data (usually a database). And it’s meant to be used by a frontend, such as Yesod or Snap. Besides having a nice test suite, it’s also unencumbered by the minutia of dealing with databases and thus is easier to review. It also defines a standard test suite that every backend needs to pass.
Out-of-the-box you’ll find support for many different backends:
- The serversession-backend-persistent package allows one to use any SQL database that persistent supports, including PostgreSQL, MySQL, and SQLite.
- Through the serversession-backend-acid-state package you may use acid-state. This backend keeps sessions in memory but provides ACID guarantees using a transaction log.
- We also support Redis via the serversession-backend-redis package.
We also already officially support the most popular frontends:
- Yesod, through the serversession-frontend-yesod package. Provides a drop-in replacement for clientsession.
- Snap, through the serversession-frontend-snap package. Also provides a drop-in replacement for clientsession.
- Even plain WAI apps, through the serversession-frontend-wai package.
Adding a new backend is very straightforward, specially because there’s already a test suite for free. Adding a new frontend is a bit more complicated depending on how well your frontend’s concept of sessions maps to serversession’s. If you’d like to support your favorite backend/frontend, please send your contributions back upstream so they become official packages as well!
If you have an Yesod app, you’re probably using persistent. Changing your app to support serversession is just a matter of setting up the session storage entities and changing Yesod’s default session backend (not to be confused with a serversession backend).
To setup the entities, you’ll have to teach persistent how to set them up on your database. Please check serversession-backend-persistent’s docs for details, but it all boils down to changing your migration Template Haskell code from:
share [mkPersist sqlSettings, mkMigrate "migrateAll"]
share [mkPersist sqlSettings, mkSave "entityDefs"]
-- On Application.hs
mkMigrate "migrateAll" (serverSessionDefs (Proxy :: Proxy SessionMap) ++ entityDefs)
Changing the default session backend is even easier. Again, please read the docs, but you’ll just add the following to your
instance Yesod App:
And you’re set! Please take a look at the included example project to see how everything fits together.
One of clientsession’s biggest success was in being used by many different projects. This is desirable both for reducing duplicate community efforts and for increasing the number of eyeballs over a security-related piece of code. I’d like to make serversession equally successful in the same way, which is why it supports from the get-go both the major Haskell web frameworks that today use clientsession by default.
I’d like to invite your criticism and feedback. And your success stories as well!
Happy New Year!