npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

hono-session

v1.1.0

Published

Hono cookie-based session middleware

Downloads

319

Readme

Hono-session

import session from 'hono-session'

const app = new Hono()
  .use(session())
  .post('/login', async c => {
    c.session.userId = '...'
    // or
    const session = c.get('session')
    session.userId = '...'
  })
  .post('/logout', async c => {
    c.session = null
    // or
    c.set('session', null)
  })
  .get('/me', async c => {
    const userId = c.session.userId
    if (!userId) return c.text('Unauthorised', 401)
    return c.text(userId)
  })

Middleware options

maxAge: number (default 1 day)

Session expiry in seconds.

cookieName (default "sid")

The name of the cookie holding the session id or data.

existsCookieName (default "sx")

The name of a cookie that is set to "1" if a session exists, auth checks can be assumed to fail if not set. This will always have httpOnly=false regardless of cookieOptions.

cookieOptions (default secure, httpOnly, path=/)

Options for both cookie types, passed directly to hono's setCookie().

Session properties

renew(maxAge?: number)

Update the session expiration.

regenerate() => Promise

Generate a new session ID. This should be called when a user logs in to prevent session fixation attacks. The old session entry will be deleted from the store, not overwritten.

flash(key, value) => void

Save a value to the session that will be removed after the next request.

reflash() => void

Persist flashed values for an additional request, useful for redirects.

hasChanged: boolean

If the session data has been modified since it was loaded.

isNew: boolean

If the session has not been saved yet.

Stateless by default

Session data is encrypted and stored in the client's cookies, no database required.

session({
  secret: '...', // Encryption key, must be at least 32 characters
  encoder: { // Custom encryption functions
    encode (data, secret) { /* object -> string */ },
    decode (data, secret) { /* string -> object */ },
  },
})

secret and encoder are both optional, iron-webcrypto will be used by default.

If a secret is not provided then a random one will be generated and sessions will only be valid until the server restarts.

External stores

Session data can also be saved into a file or database table, the cookie will be set to a random UUID.

const databaseStore = { 
  async get (id, c) {
    return db.query('SELECT data FROM sessions WHERE id = $id', { id })
  },
  async set (id, data, c) {
    await db.query(
      'INSERT INTO sessions (id, data) VALUES ($id, $data) ON CONFLICT DO UPDATE SET data = $data',
      { id, data }
    )
  },
  async delete (id, c) {
    await db.query('DELETE FROM sessions WHERE id = $id', { id })
  },
}

session({
  store: databaseStore,
})

Hono context is available as the last argument if you need to store additional information like user agent or client IP address.

This is the same interface as js Maps, you can use a Map directly as an in-memory store:

const memoryStore = new Map()

session({
  store: memoryStore,
})

Typescript

If you only use sessions on specific routes, custom properties can be passed into the middleware as a generic argument:

app.use(
  session<{
    userId?: string
  }>()
)

For larger apps you probably want to set it globally instead:

import 'hono-session/global'

declare module 'hono-session' {
  export interface Session {
    userId?: string
  }
}

Custom middleware can also refine the session type:

import { createMiddleware } from 'hono/factory'

function verifySession () {
  return createMiddleware<{
    Variables: {
      user: User,
      Session: {
        userId: string
      }
    }
  }>(async (c, next) => {
    const userId = c.session.userId
    if (!userId) return c.text('Unauthorised', 401)

    const user = await getUser(userId)
    c.set('user', user)

    await next()
  })
}

app.get('/me', async c => {
  c.session.userId
  //        ^? string | undefined
})

app.get('/me', verifySession(), async c => {
  c.session.userId
  //        ^? string
  const user = c.get(user)
  return c.json(user)
})