Sessions
Reading the current session and scoping queries and inserts to the logged-in user.
After setting up authentication, you can read the current session to scope queries and inserts to the logged-in user.
Get the session and user ID
const session = useSession();const sessionUserId = session?.user_id ?? null;const session = useSession();const sessionUserId = session?.user_id ?? null;const session = getSession();const sessionUserId = $derived(session?.user_id ?? null);const session = db.getAuthState().session;const sessionUserId = session?.user_id ?? null;Read rows for the current user
Use the session's user ID with .where() to scope queries to the current user.
const ownedTodos =
useAll(sessionUserId ? app.todos.where({ owner_id: sessionUserId }) : undefined) ?? [];const ownedTodos = useAll(sessionUserId ? app.todos.where({ owner_id: sessionUserId }) : undefined);const ownedTodos = new QuerySubscription(
sessionUserId ? app.todos.where({ owner_id: sessionUserId }) : undefined,
);const ownedTodos = sessionUserId
? await db.all(app.todos.where({ owner_id: sessionUserId }))
: [];Insert a user-owned row
Insert a row with the session's user ID to associate it with the current user.
function addOwnedTodo(title: string) {
if (!sessionUserId) return;
db.insert(app.todos, {
title,
done: false,
owner_id: sessionUserId,
});
}function addOwnedTodo(title: string) {
if (!sessionUserId) return;
db.insert(app.todos, {
title,
done: false,
owner_id: sessionUserId,
});
}function addOwnedTodo(title: string) {
if (!sessionUserId) return;
db.insert(app.todos, {
title,
done: false,
owner_id: sessionUserId,
});
}function addOwnedTodo(title: string) {
if (!sessionUserId) return;
db.insert(app.todos, {
title,
done: false,
owner_id: sessionUserId,
});
}Use whatever column fits your schema — owner_id, author_id, assignee_id, etc. — the column names have no special significance.
Session identity and authorship
Jazz also records authorship and edit times through the edit metadata magic columns $createdBy,
$createdAt, $updatedBy, and $updatedAt.
See Queries for how to select and filter on these columns.
On clients, writes are automatically attributed to the current session user. On backends,
await context.forRequest(...) and context.forSession(...) both run as that user for permissions and
also stamp authorship as that user.
Attribution without impersonation
Sometimes backend code should keep backend-level permissions but still record a user's identity in edit metadata. Use the attribution helpers for this:
context.withAttribution(userId)— stamps writes as the given user IDcontext.withAttributionForSession(session)— derives authorship from a resolved sessionawait context.withAttributionForRequest(req)— derives authorship from an authenticated request
export async function createAttributedHandles(req: Request) {
const syntheticSession = {
user_id: "user_123",
authMode: "external" as const,
claims: {},
};
return {
backendDb: context.asBackend(schemaApp),
attributedDb: context.withAttribution("user_123", schemaApp),
attributedSessionDb: context.withAttributionForSession(syntheticSession, schemaApp),
attributedRequestDb: await context.withAttributionForRequest(req, schemaApp),
};
}These differ from forRequest / forSession in one key way:
await context.forRequest(...)/context.forSession(...)change both permission evaluation and authorshipwithAttribution*only changes authorship — permissions stay at the backend levelasBackend()and unscopeddb()writes default tojazz:system
Client-side filters are useful for UX, but they don't enforce access control on their own. Use Permissions to define row-level policies that control which sessions can read or mutate a row.
Next steps
- Permissions — define row-level access policies using session identity
- Queries — subscribe to data and filter by user
- Writing Data — insert, update, and delete rows