← Blog

Building lzear.org

The stack

I rebuilt this site from scratch with a few goals: modern React full-stack without Next.js, real-time data without WebSockets, and everything deployable to a cheap VPS.

Here's what I landed on.

Remix + Bun

Remix (technically React Router v7 now) handles routing and SSR. Bun runs the server — no Node.js.

The entry point is four lines:

const handler = createRequestHandler(
  () => import('./build/server/index.js'),
  process.env.NODE_ENV,
)
 
Bun.serve({ port: 3000, fetch: handler })

Electric SQL for real-time

The voting app on this site shows live results as votes come in. I'm using Electric SQL for this — it syncs Postgres state to the browser reactively.

const { data: votes } = useShape<VoteRow>({
  url: `${electricUrl}/v1/shape`,
  params: { table: 'votes', where: `poll_id = '${pollId}'` },
})

When anyone submits a vote, every open browser tab updates without any WebSocket management code on my end.

Drizzle ORM

Type-safe DB queries that look like SQL:

const polls = await db
  .select()
  .from(polls)
  .orderBy(desc(polls.createdAt))

Schema lives in code, migrations are generated by drizzle-kit.

Better Auth

Username/password + OAuth (GitHub, Google) + anonymous guest sessions. More complex than I'd like but flexible.

Infrastructure

The whole infrastructure is in infra/ in the repo. No Terraform, no Kubernetes — just shell scripts and compose files.

What I'd change

The auth setup is more complex than it needs to be. Better Auth's username plugin piggybacks on email/password infrastructure, which means storing placeholder emails. Fine for now.

Electric SQL adds a sidecar container which means each environment (prod, each preview) runs three containers. Worth it for the DX.

← older

Spotipiano

Comments

No comments yet.

Sign in to leave a comment.