Implementing a Session Store


Learning Axum

When I began learning Scala, I decided to build a blog as a way to familiarize myself with the ecosystem. Since the problem domain was straightforward, it seemed like a good fit. However, when I tried to do the same thing in Rust, I lost interest once I realized the amount of work required to create and maintain a user interface. That’s when I discovered Zola, a Static Site Generator written in Rust, which I plan to write about in a future blog post.

Recently, while exploring the login process in axum, I came across the SessionStore trait. This piqued my interest, and I decided to create my own SessionStore as a mini project.

“Author’s opinion”: Easiest way to learn about a new code base in any language is to look at tests for that code base.

I came across an implementation on GitHub that looked like a good starting point. After reviewing the code, I felt confident enough to begin my own implementation. The axum ecosystem is quite robust, and there are numerous existing SessionStore implementations available.

Requirements for SessionStore Implementation

  • minimal config / setup
  • persistent / storage layer with simple api

I had come across Sled on Hacker News and was intrigued by its capabilities. After reviewing its examples and API, I found it to be straightforward and easy to set up, making it the perfect fit for my requirements.

You can find my implementation here, where I’d like to draw attention to how we handle store() and load() functions for the session. Let’s take a closer look at how the store() function is implemented.

    async fn store_session(&self, session: Session) -> Result<Option<String>> {
        let id = session.id();
        let json_cookie: String = serde_json::to_string(&session)?;
        let _ = self.store.insert(id, IVec::from(&json_cookie[..]))?;
        Ok(session.into_cookie_value())
    }

We serialize the Session using serde and then store this json value using session.id() as the key.

“Author’s opinion”: It’s refereshing to see most of the community embracing one serialization / de-serialization framework.

Let’s look at how load() is implemented.

    async fn load_session(&self, cookie_value: String) -> Result<Option<Session>> {
        let id = Session::id_from_cookie_value(&cookie_value)?;

        match self.store.get(id)? {
            Some(data) => {
                let data = std::str::from_utf8(&data[..])?;
                let json_data: Session = serde_json::from_str(data)?;
                Ok(Some(json_data))
            }
            None => Ok(None),
        }
    }

We extract the id from the cookie_value and use it to load the Session data.

“Author’s opinion”: The documentation for SessionStore trait includes all the lifetime annotations, it can be disconcerting when you read it for the first time.

For ex:

    fn load_session<'life0, 'async_trait>(
        &'life0 self,
        cookie_value: String
    ) -> Pin<Box<dyn Future<Output = Result<Option<Session>>> + Send + 'async_trait>>
    where
        'life0: 'async_trait,
        Self: 'async_trait;

After conducting some research, I stumbled upon an insightful article titled “Async fn in Traits are Hard”, which helped me to better understand the SessionStore API. I also recommend reading this article for more information.

“Author’s opinion”: It’s important to be explicit about multiple async runtimes in the project.

During my work on the project and while reading Axum’s documentation on its Tokio usage, I was under the impression that there was only one runtime, which would also be used during testing. However, when I attempted to debug a test that I had written, I discovered that I was using a different async runtime for testing. This made me go back and do more research on async-std. I accept partial responsibility for this oversight.

Overall, my experience working on this project was very positive. Please feel free to contact me through one of my social media links if you notice any errors.

axum  rust 

“Rumi Quote”: Would you become a pilgrim on the road of love? The first condition is that you make yourself humble as dust and ashes.

See also