camelon

Sessions

The session arg is a signed cookie store, wired into every handler and middleware. Mutations commit before the response. There is no manual save.

import { redirect } from 'camelon';
import type { PostFunction } from 'camelon';

export type PostInput = { body: { email: string; password: string } };

export const post: PostFunction<PostInput> = async ({ body, session }) => {
  const user = await authenticate(body);
  session.set('userId', user.id);
  session.flash('toast', 'Welcome back'); // read once on the next page
  throw redirect('/dashboard');
};
import { redirect } from 'camelon';
import type { PostFunction } from 'camelon';

export const post: PostFunction = async ({ session }) => {
  session.destroy(); // clears state, expires the cookie
  throw redirect('/');
};

API

Method Effect
get(key) read a value
set(key, val) write a value
has(key) check a key
unset(key) remove a key
flash(key, val) write a one-read value
destroy() clear state, expire the cookie

Type the store by declaring session on Registerget/set are then keyed and valued from it, with no per-call type argument:

declare module 'camelon' {
  interface Register {
    session: { userId: string };
  }
}

session.set('userId', user.id);      // value typed
const id = session.get('userId');    // string | undefined

Guarding a section

Check the session in a _middleware.ts:

import { redirect } from 'camelon';
import type { MiddlewareFunction } from 'camelon';

const requireAuth: MiddlewareFunction = ({ session }) => {
  if (!session.has('userId')) throw redirect('/login');
};

export const middleware = [requireAuth];
export default function Dashboard() {
  return <h1>Welcome back</h1>;
}

The secret

Set SESSION_SECRET to sign cookies. In dev without one, camelon uses an ephemeral per-boot secret and warns. In production without one, the first session use throws. There is no default secret.

Custom store

Pass your own store for max-age or key rotation. createSessionStore is on the camelon/session subpath.

import { createSessionStore } from 'camelon/session';

await createHandler({
  routes: manifest,
  session: createSessionStore({
    secrets: [process.env.SESSION_SECRET!],
    maxAge: 60 * 60 * 24,
  }),
});

CSRF

camelon checks the Origin header on POST, PUT, PATCH, and DELETE in production, blocking cross-origin writes. Allow extra origins for third-party callbacks, or disable with ['*'].

await createHandler({
  routes: manifest,
  csrf: { trustedOrigins: ['https://pay.example.com'] },
});

There is no raw cookies arg. For a one-off cookie, read request.headers.get('cookie') or write response.header('set-cookie', ...).