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)
})
Session expiry in seconds.
The name of the cookie holding the session id or data.
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.
Options for both cookie types, passed directly to hono's setCookie()
.
Custom options for the exists cookie, will be merged with cookieOptions
. If you have hono on "api.example.com" and a static frontend on "example.com" or another subdomain you will have to set existsCookieOptions: { domain: 'example.com' }
to be able to read it from document.cookie
.
Update the session expiration.
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.
Save a value to the session that will be removed after the next request.
Persist flashed values for an additional request, useful for redirects.
If the session data has been modified since it was loaded.
If the session has not been saved yet.
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.
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,
})
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)
})