Primate Logo Primate

Sessions

Sessions let you persist state across requests: authentication, user preferences, shopping carts, or any other server-side data you want tied to a client. Primate manages session cookies, validates session data, and ensures changes are committed only if your route succeeds.

Configuration

Sessions are configured in config/session.ts. By default Primate uses an in-memory store, but you may also use any store defined in stores or define one directly in config/session.ts.

Session options

Option Default Description
cookie.httpOnly true mark cookie as HttpOnly
cookie.name "session_id" name of the session cookie
cookie.path "/" path for which the cookie is valid
cookie.sameSite "Lax" SameSite cookie policy
store default in-memory store store managing sessions

Whether the session cookie should be marked HttpOnly (hidden from client-side JavaScript).

The name of the session cookie.

The path for which the cookie is valid.

The SameSite cookie policy: "Strict", "Lax", or "None".

store

The session store. By default Primate uses a store managed in memory, but you can use any store defined in stores or define one directly in config/session.ts. The store will be used for both validating the data and persisting it to a database of your choice.

The store must include a session_id field of type UUID. Primate uses this to associate each stored record with the session cookie.

Example

First create the store inside stores.

import p from "primate/pema";
import store from "primate/store";

export default store({
  id: p.primary,
  session_id: p.string.uuid(),
  user_id: p.number,
  last_active: p.date,
  // additional fields as needed
});

Then refer to it in your configuration file.

import session from "primate/config/session";
import Session from "#store/Session";

export default session({
  store: Session,
});

Session facade

The session facade is the API used in routes to interact with session state. It hides cookie handling and persistence details, exposing a simple interface to create, read, update, and destroy sessions.

method / property description
id current session ID, if one exists
exists whether a session is active
create(initial) start a session with initial data; generates a new ID
get() return current session data; throws if none
try() return data if a session exists, otherwise undefined
set(data) replace session data or derive from the previous state
destroy() end the session and clear the cookie

Usage in routes

Import the session facade via #session. It is bound to the session store you used in config/session.ts.

import session from "#session";
import route from "primate/route";

route.get(() => {
  if (!session.exists) {
    session.create({ user_id: 42 });
  }

  const data = session.get();
  return `User ${data.user_id} last active at ${data.last_active.toISOString()}`;
});

SessionFacade reference

interface SessionFacade<T> {
  readonly id: string | undefined;
  readonly exists: boolean;

  create(initial?: T): void;
  get(): Readonly<T>;
  try(): Readonly<T> | undefined;

  set(next: ((previous: Readonly<T>) => T) | T): void;

  destroy(): void;
}

Validation

Primate validates data passed to create and set using the provided session store.

import p from "primate/pema";
import store from "primate/store";

export default store({
  id: p.primary,
  session_id: p.string.uuid(),
  token: p.string.min(10),
});
import session from "#session";
import route from "primate/route";

route.post(() => {
  // Throws if token is shorter than 10 characters
  session.set({ token: "abc" });
});

Other operations like get, try or destroy don't take input and do not trigger validation.

This ensures your session store never contains malformed data.

Previous
Validation
Next
Stores