Skip to content

Creating a custom storage driver

Joe Sweeney edited this page Oct 28, 2024 · 1 revision

In most cases, hono_sessions storage drivers should consist of just a single file. You can either create a driver just for use in your own application, or you can create a package for the driver and share it with the community!

As an example, let's create a driver for MongoDB. I'm going to use the Deno runtime for this because it's the simplest and is what I'm most familiar with 😄

First, let's get an instance of MongoDB running on our local machine using Docker. The following command will create an instance of Mongo then delete it automatically after we're done:

docker run --rm -p 27017:27017 --name mongo-sessions mongo:latest

Somewhere in your project, we'll need to create a .ts file that extends the Store interface. Let's create a file called MongoStore.ts in our project root:

import { Store, SessionData } from 'npm:hono-sessions' // Import hono-sessions types
import { Db, Collection } from 'npm:mongodb' // Import MongoDB types

class MongoStore implements Store {
  db: Db
  sessions: Collection

  constructor(db: Db, collectionName = 'sessions') {
    this.db = db
    this.sessions = db.collection(collectionName)
  }

  async getSessionById(sessionId: string) {
    const session = await this.sessions.findOne({ id: sessionId })
    return session ? session.data : null
  }

  async createSession(sessionId: string, initialData: SessionData) {
    await this.sessions.replaceOne(
      {
        id: sessionId
      },
      {
        id: sessionId,
        data: initialData
      },
      {
        upsert: true
      }
    )
  }

  async persistSessionData(sessionId : string, sessionData : SessionData) {
    await this.sessions.replaceOne(
      { id: sessionId },
      {
        id: sessionId,
        data: sessionData
      },
      { upsert: true }
    )
  }

  async deleteSession(sessionId : string) {
    await this.sessions.deleteOne({ id: sessionId })
  }
}

export default MongoStore

Now, we'll create a new Hono app called app.ts that uses our new storage driver:

import { Hono } from 'npm:hono'
import { html } from 'npm:hono/html'
import { sessionMiddleware, Session } from 'npm:hono-sessions'
import MongoStore from './MongoStore.ts'
import { MongoClient } from 'npm:mongodb'

// Create new Mongo client or use an existing client
const client = new MongoClient('mongodb://localhost:27017')
await client.connect()

// Create a Mongo database, or use an existing db
const db = client.db('sessions-mongo')

// Create a store using our new storage driver
const store = new MongoStore(
  db
)

type SessionDataTypes = {
  'counter': number
}

const app = new Hono<{
  Variables: {
    session: Session<SessionDataTypes>,
    session_key_rotation: boolean
  }
}>()

app.use('*', sessionMiddleware({
  store,
  encryptionKey: 'password_at_least_32_characters_long', // Required for CookieStore, recommended for others
  expireAfterSeconds: 900, // Expire session after 15 minutes of inactivity
  cookieOptions: {
    sameSite: 'Lax', // Recommended for basic CSRF protection in modern browsers
    path: '/', // Required for this library to work properly
    httpOnly: true, // Recommended to avoid XSS attacks
  },
}))

app.get('/', (c) => {
  const session = c.get('session')

  session.set('counter', (session.get('counter') || 0) + 1)

  return c.html(`<h1>You have visited this page ${ session.get('counter') } times</h1>`)
})

Deno.serve(app.fetch)

That's it! Let's test it by running the script with Deno. In a separate terminal from the Mongo process, run this command:

deno run -A app.ts

Now, visit http://localhost:8000 and you should see a visit counter that increments by 1 every time you refresh the page!

Clone this wiki locally