Guestbook Example
This capsule shows an intentionally shared feed. Guests and signed-in users can post. Everyone who can use the app can read the same entries.
What It Shows
- An
entriestable withbody,authorId: userId(),authorName, andauthorPicture. - A shared
entriesquery ordered by newest first. - A
signmutation that trims and bounds user input. - Server-side authorship from
ctx.auth, not from client-submitted fields. - A Preact UI using
useAuth,createClient<typeof app>(), and<SignInWithGoogle />.
Server Pattern
Use shared feeds when every user can read the same rows:
queries: {
entries: query(async (ctx) => ctx.db.entries.withIndex("by_creation").order("desc").take(50))
}
Still keep writes server-authoritative:
const author = ctx.auth.requireIdentity();
await ctx.db.entries.insert({
body: trimmed,
authorId: author.userId,
authorName: author.displayName,
authorPicture: author.picture ?? ""
});
Do not accept authorId, authorName, authorPicture, or other trusted metadata from the client.
Sharing comes from the query's lack of an owner filter. It does not use a shared guest ID or a fake global user. The authorId reference follows a guest to their account on sign-in. authorName and authorPicture are stored snapshots, so reference transfer does not replace them.
To allow only signed-in accounts to post, use ctx.auth.requireSignedIn() in the sign mutation. To require sign-in before reading or writing any app data, set auth: { requireSignIn: true } on the capsule.
Run It
Run the checked-in example:
npx lakebed dev examples/guestbook
Open:
http://localhost:3000
To see shared updates from named local test identities, open:
http://localhost:3000/?lakebed_guest=alice
http://localhost:3000/?lakebed_guest=bob
Named overrides cannot upgrade to accounts. Use the default browser session with no override to test guest upgrades.
Then inspect state:
npx lakebed db dump --port 3000
npx lakebed logs --port 3000