Skip to content

Sessions

WebJs provides a Session class with a SessionStorage interface, inspired by Remix. Storage owns the session lifecycle: storage.read(cookie) → Session, storage.save(session) → cookie. Two built-in storage implementations cover most use cases.

Setup

Add session middleware in middleware.ts:

// middleware.ts
import { session, cookieSessionStorage } from '@webjsdev/server';

export default session({
  secret: process.env.SESSION_SECRET,
  storage: cookieSessionStorage(),  // or storeSessionStorage() for Redis
});

The only requirement is SESSION_SECRET (or pass secret directly), the key used to sign session cookies.

SessionStorage Implementations

cookieSessionStorage() (default)

All session data lives in the cookie itself. Stateless, with no server-side storage needed.

  • Scales horizontally with no shared storage.
  • 4 KB cookie size limit applies (after signing overhead).
  • Every response includes the full session cookie.
import { session, cookieSessionStorage } from '@webjsdev/server';

export default session({
  secret: process.env.SESSION_SECRET,
  storage: cookieSessionStorage(),
});

storeSessionStorage() (production)

Only a session ID is stored in the cookie. Session data lives in the global cache store, which is in-memory by default. Switch to Redis for horizontal scaling by calling setStore(redisStore({ url: process.env.REDIS_URL })) once at app startup.

  • No payload size limit beyond store key size.
  • Server-side invalidation: delete the store entry and the session is gone.
  • For production, use with Redis: setStore(redisStore(...)).
import { session, storeSessionStorage } from '@webjsdev/server';

export default session({
  secret: process.env.SESSION_SECRET,
  storage: storeSessionStorage(),  // uses getStore(), memory or Redis
});

Session Class API

Use getSession(req) in any server-side code (API routes, server actions, middleware):

import { getSession } from '@webjsdev/server';

const s = getSession(req);

s.get(key)

Returns the value for the key, checking both regular data and flash data.

const userId = s.get('userId');

s.set(key, value)

Sets a value. Pass null or undefined to delete the key.

s.set('userId', user.id);
s.set('role', 'admin');

s.has(key)

Returns true if the key exists in either regular or flash data.

if (s.has('userId')) { /* authenticated */ }

s.flash(key, value)

Sets a value that exists for one request only. Use for success/error messages after redirects.

s.flash('message', 'Post created!');
// Next request: s.get('message') → 'Post created!'
// Request after: s.get('message') → undefined

s.destroy()

Clears all session data and removes the cookie. Use for logout flows.

s.destroy();

s.regenerateId(deleteOld?)

Regenerates the session ID. Call after login to prevent session fixation attacks. Pass true to delete the old session entry from the store.

// After successful login:
s.set('userId', user.id);
s.regenerateId(true);  // new ID, old store entry deleted

Example: Login Flow

// app/api/login/route.ts
import { getSession } from '@webjsdev/server';
import { db } from '#db/connection.server.ts';
import { verifyPassword } from '#lib/auth.server.ts';

export async function POST(req: Request) {
  const { email, password } = await req.json();

  const user = await db.query.users.findFirst({ where: { email } });
  if (!user || !await verifyPassword(password, user.passwordHash)) {
    return Response.json({ error: 'Invalid credentials' }, { status: 401 });
  }

  const s = getSession(req);
  s.set('userId', user.id);
  s.set('role', user.role);
  s.regenerateId(true);

  return Response.json({ ok: true });
}

Example: Logout

// app/api/logout/route.ts
import { getSession } from '@webjsdev/server';

export async function POST(req: Request) {
  const s = getSession(req);
  s.destroy();
  return Response.json({ ok: true });
}

Example: Flash Messages

// In a server action after creating a post:
const s = getSession(req);
s.flash('success', 'Post published!');

// In the page that renders after redirect:
const s = getSession(req);
const message = s.get('success');  // 'Post published!', only this request

Session Options

The session() middleware accepts these options:

session({
  secret: '...',                      // required (or SESSION_SECRET env var)
  storage: cookieSessionStorage(),    // default: cookieSessionStorage()
  cookieName: 'webjs.sid',            // default: 'webjs.sid'
  maxAge: 86400_000,                  // 24 hours in ms (default)
  path: '/',                          // cookie path (default: '/')
  httpOnly: true,                     // default: true
  secure: true,                       // default: true
  sameSite: 'Lax',                    // default: 'Lax'
})

Next Steps

  • Caching: the cache store that backs server-side sessions
  • Authentication: NextAuth-style auth built on top of sessions
  • Middleware: run session checks before route handlers