TanStack Start + Better Auth + Drizzle Starter
A practical starter for building a TanStack Start app with authentication, database ORM, and modern styling.
- Devs evaluating TanStack Start with a real auth + DB setup
- Teams that want a clean foundation without a heavy UI kit
- Anyone who wants type-safe routing, typed data loaders, and a straightforward auth flow
- TanStack Start + Router file-based routing
- TanStack Query wired into router SSR (not used by default to keep the template lean)
- Better Auth with Google provider and server handler route
- Drizzle ORM schema + migrations wiring for PostgreSQL
- Tailwind CSS v4 setup
- Portless local HTTPS URLs for dev (
https://tbd.localhost) - Oxlint for linting
- Oxfmt for formatting
- Protected routes + login flow
bun install
bun run devThe dev script runs Vite through portless, so the app is available at https://tbd.localhost instead of a fixed localhost:<port> URL. On first run, portless may prompt to trust its local certificate and start the local proxy.
Create a .env.local file in the project root (see .env.schema):
DATABASE_URL=
BETTER_AUTH_SECRET=
BETTER_AUTH_URL=
GOOGLE_CLIENT_ID=
GOOGLE_CLIENT_SECRET=
.env.schema is the source of truth for env vars and is used by varlock to generate env.d.ts.
- Auth handler lives at
/api/auth/*. - Update your OAuth callback URLs to match your local and production origins (for Google locally:
https://tbd.localhost/api/auth/callback/google). - Server auth code is in
src/features/auth/server.ts. - Client auth code is in
src/features/auth/client.ts. - Server function auth middleware is in
src/features/auth/middleware.ts.
The schema entry point is src/lib/db/schema.ts.
bun run db:generate
bun run db:migrate
bun run db:push
bun run db:studio- Dev server:
bun run dev - Production build:
bun run build - Production server:
bun run start - Lint:
bun run lint - Lint with fixes:
bun run lint:fix - Format:
bun run fmt - Format (check only):
bun run fmt:check - Tests:
bun run test - Generate Drizzle migrations:
bun run db:generate - Run Drizzle migrations:
bun run db:migrate - Push schema changes:
bun run db:push - Open Drizzle Studio:
bun run db:studio
Linting is configured in .oxlintrc.json and formatting is configured in .oxfmtrc.json.
Build and run the provider-neutral Docker image with the environment variables
from .env.schema:
varlock run -- docker build \
--build-arg DATABASE_URL \
--build-arg BETTER_AUTH_SECRET \
--build-arg BETTER_AUTH_URL \
--build-arg GOOGLE_CLIENT_ID \
--build-arg GOOGLE_CLIENT_SECRET \
-t tbd .
docker run --env-file .env.local -p 3000:3000 tbdThe image runs the TanStack Start server with srvx, serves the built client
assets, listens on the provider-supplied PORT (default 3000), and includes
an HTTP health check. Varlock validates required variables during both the
image build and server startup. Run migrations before each release with
bun run db:migrate in the release environment.
railway.json is an example Railway configuration. It builds the same
Dockerfile, runs bun run db:migrate before deployment, checks / for health,
and restarts failed processes. Add every variable from .env.schema to the
Railway service; Railway makes them available to the Docker build and the
running container.
src/features/auth: auth server/client/session/middleware helperssrc/lib/db: Drizzle client + schemasrc/router.tsx: router setup, including TanStack Query SSR integrationsrc/routes: file-based routes (including protected routes)src/styles.css: Tailwind setupdocs/tech-stack: stack notes and conventionsdocs/recipes: worked examples that can be copied into the starter when needed
- Setup Shadcn/ui:
bunx --bun shadcn@latest init - Add your own routes in
src/routes - Add additional OAuth providers in
src/features/auth/server.ts - Create your app schema in
src/lib/db/schema.ts
- TanStack Start: https://tanstack.com/start
- TanStack Router: https://tanstack.com/router
- TanStack Query: https://tanstack.com/query
- Better Auth: https://better-auth.com
- Drizzle ORM: https://orm.drizzle.team
- Tailwind CSS: https://tailwindcss.com
- Portless: https://portless.sh
- Oxlint: https://oxc.rs/docs/guide/usage/linter.html
- Oxfmt: https://oxc.rs/docs/guide/usage/formatter.html