Nextbase Docs
Nextbase Docs
HomeBlogWelcome to Nextbase v5!Choose your starterGetting startedLocal Supabase per agent
TanStack + Cloudflare D1InstallationEnvironment variablesAuthenticationAgent connectionsProject structureDeployment
AuthenticationWorkspaces and the admin area
Building with agentsEmails with Resend
TroubleshootingNextbase v5
The 8 starter kitsTanStack + Cloudflare D1

Project structure

The monorepo layout of the TanStack + Cloudflare D1 starter and where to make common changes.

The repository is a pnpm workspace orchestrated by Turborepo. Five apps and two shared packages live under apps/* and packages/*. Run every command from the repository root.

routes.ts
app.ts
realtime-room.ts
auth-rate-limiter.ts
wrangler.jsonc
.env.example
vite.config.ts
react-router.config.ts
playwright.config.ts
vitest.config.ts
vitest.integration.config.ts
worker-configuration.d.ts
routes.ts
root.tsx
wrangler.jsonc
.env.example
vite.config.ts
react-router.config.ts
worker-configuration.d.ts
index.ts
handlers.ts
outbox.ts
stripe.ts
media.ts
unkey.ts
account-deletion.ts
observability.ts
wrangler.jsonc
.env.example
client.ts
drizzle.config.ts
package.json
pnpm-workspace.yaml
turbo.json
AGENTS.md

The five apps

PackagePathWhat it is
@nextbase/webapps/webThe customer-facing React Router app and the Worker that serves it, including the User MCP. Vite builds it with @cloudflare/vite-plugin; wrangler.jsonc is the Worker config.
@nextbase/adminapps/adminThe administrator app, a second React Router app and Worker on its own origin (port 5174 locally). It has its own routes, login and Better Auth secret, reuses the web app's server modules through the ~ alias, and serves the Admin MCP.
@nextbase/jobsapps/jobsThe background Worker: Queue consumer, dead-letter consumer, /health endpoint and two crons (* * * * * for outbox recovery and cleanup, 0 3 * * * for daily reconciliation and retention).
@nextbase/databaseapps/databaseDrizzle schema, generated D1 types, the hand-written SQL migrations and the seed.
@nextbase/emailapps/emailThe 41 React Email templates and the renderer the jobs Worker calls. pnpm dev also runs its preview on port 3001.

packages/contracts holds the shared zod schemas and types: auth (Principal, WorkspaceContext, roles), job envelopes, problem details, observability helpers and workspace contracts. packages/typescript-config holds the shared tsconfig bases.

infra/admin-domain and infrastructure/demo-domain are two small Cloudflare Pages projects that proxy the demo's custom domains to the admin and web Workers through a service binding. You only need them if you attach a domain the same way.

Where to make changes

Routes and pages

Web routes are declared in apps/web/app/routes.ts and implemented in apps/web/app/routes/. Public marketing, blog, changelog, docs, feedback and roadmap pages share routes/public.tsx. Signed-in pages sit under routes/app-layout.tsx: dashboard.tsx, workspaces.tsx, workspace.tsx (/workspace/:workspaceSlug/*), project.tsx and user.tsx (/user/*). API routes are the api-*.ts files. impersonate.ts and stop-impersonating.ts are the two halves of the cross-origin impersonation handoff. Add a route in routes.ts and a matching file next to the others.

Admin routes are declared in apps/admin/app/routes.ts: login.tsx, the password and verification pages, admin.tsx (/app-admin/*, one module that switches on the section), api-auth.ts, api-media.ts, api-health.ts and api-admin-insights.ts. The web app has no /app-admin routes.

Reusable UI lives in apps/web/app/components/, with components/ui/ for primitives. Hooks are in app/hooks/, small utilities in app/lib/. The admin app imports these through the same ~ alias.

Server logic and data access

Everything that runs only on the Worker is in apps/web/app/server/*.server.ts. Start with session.server.ts for the principal, authorization.server.ts for workspace capabilities, repositories.server.ts for tenant-scoped reads and writes, workspaces.server.ts and project-mutations.server.ts for the core domain, payments.server.ts for Stripe and queue.server.ts for nudging the Queue. The admin app adds apps/admin/app/server/admin-magic-link.server.ts for issuing magic links to users from the console.

Keep the rule from AGENTS.md: resolve a WorkspaceContext from the session and route slug first, then pass it into repositories. Mutations that need atomicity use a single D1 batch() and write audit and outbox rows in that batch.

Schema and migrations

Edit the Drizzle tables in apps/database/src/schema/ (auth.ts, core.ts, billing.ts, content.ts, system.ts). Run pnpm db:generate to produce SQL under apps/database/migrations/generated/, then add the reviewed statements as the next numbered file in apps/database/migrations/ (the folder Wrangler applies, currently 0001 to 0025; 0025_user_admin_mcp.sql creates the mcp_records table for agent connections). Apply with pnpm db:migrate and run pnpm test:integration.

Migrations are forward-only. FTS5 search tables, the development email inbox and triggers live in the hand-written migrations rather than in Drizzle tables.

Authentication

apps/web/app/server/auth.server.ts for Better Auth options, plugins and hooks; auth-handler.server.ts and auth-route.server.ts for request handling; impersonation.server.ts for the read-only impersonation rule; apps/web/workers/auth-rate-limiter.ts for the limiter. The admin app reuses all of these with ADMIN_APP=true. Details are on the authentication page.

Emails

Templates are in apps/email/emails/, rendered by apps/email/src/render.tsx. Sending starts in apps/web/app/server/email.server.ts (enqueueEmail) and finishes in apps/jobs/src/handlers.ts (sendEmail), which chooses Resend or the local inbox. To add an email, add the template, then call enqueueEmail with its name and props.

Background jobs and workers

Job types and the envelope schema are in packages/contracts/src/jobs.ts. The dispatcher is dispatchJob in apps/jobs/src/handlers.ts; leases, receipts and retries are in outbox.ts. A new job needs a type, a handler case, a stable idempotency key, and a producer that inserts the outbox row from the web or admin app. The Durable Objects (RealtimeRoom, AuthRateLimiter) are in apps/web/workers/; the admin and jobs Workers reach them through script_name bindings, as apps/jobs/AGENTS.md describes.

MCP

apps/web/src/mcp/ with core.ts, adapter.ts and d1-store.ts, mounted by both apps/web/workers/app.ts and apps/admin/workers/app.ts. See agent connections.

Tests

KindWhereCommand
Unit tests*.test.ts next to the code in every packagepnpm test
D1 integration tests (workerd and Miniflare)apps/web/test/*.integration.test.tspnpm test:integration
Browser smoke tests (web and admin)apps/web/e2e/*.spec.tspnpm test:e2e
Everything plus lint, format, types and buildpnpm verify

CI (.github/workflows/ci.yml) runs pnpm verify, pnpm test:integration and the Playwright suite on every pull request. agent-config.yml checks that the generated AI tool configuration is in sync (pnpm agents:check). Use pnpm changeset to describe a user-visible change; release.yml maintains the version PR.

Configuration files

apps/web/wrangler.jsonc, apps/admin/wrangler.jsonc and apps/jobs/wrangler.jsonc define bindings and vars per environment. The root package.json holds the scripts you will use most (db:*, cf:*, admin:dev, verify). turbo.json wires build, dev, test and test:integration across packages. Lint and format use .oxlintrc.json and .oxfmtrc.json.

Agent connections

The User MCP and Admin MCP in the TanStack + Cloudflare D1 starter: endpoints, tools, OAuth behaviour and where the code lives.

Deployment

Provision Cloudflare resources, align the web, admin and jobs Wrangler configs, apply D1 migrations and deploy the three Workers.

On this page

The five appsWhere to make changesRoutes and pagesServer logic and data accessSchema and migrationsAuthenticationEmailsBackground jobs and workersMCPTestsConfiguration files