remix/session-middleware
The remix/session-middleware package provides middleware that connects a cookie and a session storage backend to your router. It handles reading the session from the incoming request and saving any changes to the outgoing response automatically.
Installation
The remix/session-middleware package is included with Remix. No additional installation is required.
import { session } from 'remix/session-middleware'session(cookie, storage)
Creates middleware that manages sessions for every request.
function session(cookie: Cookie, storage: SessionStorage): MiddlewareParameters:
| Parameter | Type | Description |
|---|---|---|
cookie | Cookie | A cookie created with createCookie() from remix/cookie. Defines the cookie name, signing secrets, and attributes (httpOnly, secure, sameSite, maxAge, etc.). |
storage | SessionStorage | A session storage backend (cookie storage, filesystem, memory, Redis, Memcache, or a custom implementation). |
Returns: A Middleware function that can be added to a router.
How It Works
The session middleware runs on every request and performs the following steps:
- Read -- Parses the session cookie from the incoming
Cookieheader and loads the session data from the storage backend usingstorage.read(cookieValue). - Set context -- Makes the session available to downstream middleware and handlers via
context.get(Session). - Wait -- Calls
next()and waits for the handler (and any downstream middleware) to complete. - Save -- After the handler returns a response, calls
storage.save(session)to persist any changes (new values, unset keys, flash consumption, regenerated IDs, or destruction). - Set cookie -- Adds a
Set-Cookieheader to the response with the updated cookie value.
Request ──> [Parse Cookie] ──> [Load Session] ──> [Handler] ──> [Save Session] ──> [Set Cookie] ──> ResponseAccessing the Session
Use the Session context key from remix/session to access the session in any handler or middleware that runs after the session middleware.
import { Session } from 'remix/session'
router.map(route, async ({ context }) => {
let session = context.get(Session)
// Read values
let userId = session.get('userId')
// Write values
session.set('lastVisit', Date.now())
// Flash messages
session.flash('notice', 'Settings saved.')
// The session middleware will automatically save these changes
return new Response('OK')
})Auto-Save Behavior
The session middleware saves automatically after every request. You do not need to call a save method manually. This includes:
- New values set with
session.set(key, value). - Removed values cleared with
session.unset(key). - Consumed flash values that were read with
session.get(key). - Regenerated IDs from
session.regenerateId(). - Destroyed sessions from
session.destroy()--- the storage entry is deleted and the cookie is cleared.
If the session was not modified during the request, the middleware skips the save step for efficiency. The Set-Cookie header is still included to refresh the cookie's maxAge if needed.
Middleware Ordering
The session middleware must come before any middleware or handler that accesses the session. A typical ordering:
import { createRouter } from 'remix/fetch-router'
import { session } from 'remix/session-middleware'
import { auth } from 'remix/auth-middleware'
import { formData } from 'remix/form-data-middleware'
let router = createRouter({
middleware: [
formData(), // Parse form data
session(sessionCookie, sessionStorage), // Load session
auth({ schemes: [sessionScheme] }), // Auth reads from session
],
})The auth middleware depends on the session being loaded, so it must come after session().
Complete Example
// app/session.ts
import { createCookie } from 'remix/cookie'
import { createCookieSessionStorage } from 'remix/session/cookie-storage'
export let sessionCookie = createCookie('__session', {
httpOnly: true,
secure: process.env.NODE_ENV === 'production',
sameSite: 'Lax',
secrets: [process.env.SESSION_SECRET!],
maxAge: 60 * 60 * 24 * 7, // 1 week
})
export let sessionStorage = createCookieSessionStorage(sessionCookie)// app/server.ts
import { createRouter } from 'remix/fetch-router'
import { session } from 'remix/session-middleware'
import { Session } from 'remix/session'
import { sessionCookie, sessionStorage } from './session.ts'
let router = createRouter({
middleware: [
session(sessionCookie, sessionStorage),
],
})
// Set a value
router.map(loginAction, async ({ context }) => {
let s = context.get(Session)
s.set('userId', user.id)
s.regenerateId()
return new Response(null, { status: 302, headers: { Location: '/' } })
})
// Read a value
router.map(homeRoute, async ({ context }) => {
let s = context.get(Session)
let userId = s.get('userId')
// ...
})
// Destroy the session
router.map(logoutAction, async ({ context }) => {
let s = context.get(Session)
s.destroy()
return new Response(null, { status: 302, headers: { Location: '/login' } })
})Related
- remix/session -- Session class API and storage backends
- remix/cookie -- Cookie creation and configuration
- Sessions & Cookies -- Conceptual guide
- Middleware -- How middleware works in Remix