Todo Example
This capsule shows personal todo lists for protected guests and signed-in accounts. Each browser session starts with its own list. Google sign-in keeps that list with the account.
What It Shows
- A
todostable withtext,done, andownerId: userId(). - A
todosquery filtered by the user ID fromctx.auth.requireIdentity(). - An
addTodomutation that cleans input before insert. - A
setTodoDonemutation that checks row ownership before update. - A
clearDonemutation that deletes only the current user's completed rows. - A Preact UI using
useAuth,createClient<typeof app>(), and<SignInWithGoogle />. - A pure shared helper for todo text normalization.
Server Pattern
queries: {
todos: query(async (ctx) => {
const { userId } = ctx.auth.requireIdentity();
return ctx.db.todos
.withIndex("by_owner", (q) => q.eq("ownerId", userId))
.order("desc")
.collect();
})
}
Use this pattern whenever rows belong to a single user. The client should not receive rows it does not own.
For mutations, fetch the row and check ownership before changing it:
const { userId } = ctx.auth.requireIdentity();
const todo = await ctx.db.todos.get(id);
if (!todo || todo.ownerId !== userId) {
return;
}
await ctx.db.todos.update(id, { done });
The userId() field makes reference transfer automatic after a verified guest upgrade. No hook is needed for todos. Guest todos join any existing account todos. The field does not replace ownership checks.
Set auth: { requireSignIn: true } in the server to block data access until sign-in. For a single account-only operation, use ctx.auth.requireSignedIn() instead. The client uses isGuest and isSignedIn for UI state.
Clearing browser data or an expired guest session ends guest access. Sign in before that happens to keep account access to the todos. Local todos reset when the dev server restarts.
Run It
Run the checked-in example:
npx lakebed dev examples/todo
Open:
http://localhost:3000
To compare two named local test users, open:
http://localhost:3000/?lakebed_guest=alice
http://localhost:3000/?lakebed_guest=bob
Named overrides cannot upgrade to an account. Remove the query parameter to test guest sign-in and transfer. If you set a CLI override with npx lakebed auth as alice, run npx lakebed auth reset first.
Then inspect state:
npx lakebed db dump --port 3000
npx lakebed logs --port 3000