Step 1: Project Setup
import { Steps, FileTree, Aside } from ‘@astrojs/starlight/components’;
Create the Project
Section titled “Create the Project”Run create-cruz-app to scaffold a new CruzJS project:
npx create-cruz-app my-todoscd my-todosnpm installThis scaffolds a complete application with @cruzjs/core (framework runtime), @cruzjs/start (auth, UI components, organizations), React Router v7, Drizzle ORM, and Inversify dependency injection.
Start the Dev Server
Section titled “Start the Dev Server”cruz devOpen http://localhost:5000. You should see the CruzJS welcome page.
The dev server uses Vite with Cloudflare Workers compatibility via Miniflare — your local environment behaves exactly like production.
Project Structure
Section titled “Project Structure”Here is the directory layout you start with:
The two most important files are:
src/server.cloudflare.ts
Section titled “src/server.cloudflare.ts”This is the Cloudflare Worker entry point. It bootstraps the DI container, registers modules, and handles tRPC + page requests:
import * as schema from './database/schema';import { createCruzApp } from '@cruzjs/core';import { StartModule } from '@cruzjs/start/start.module';
export default createCruzApp({ schema, modules: [ StartModule, // auth, sessions, org management // your modules go here ], pages: () => import('virtual:react-router/server-build'),});Every feature module you create gets added to modules: [...] here.
src/database/schema.ts
Section titled “src/database/schema.ts”The Drizzle migration runner discovers tables by reading this barrel file:
// Re-export framework tablesexport * from '@cruzjs/start/database/schema';
// Your feature tables go here// export * from '../features/todos/todos.schema';Every *.schema.ts file must be re-exported here or Drizzle won’t generate migrations for it.
cruz.config.ts
Section titled “cruz.config.ts”Declares Cloudflare bindings (D1, KV, R2) and per-environment variables:
import { defineConfig } from '@cruzjs/cli/config';
export default defineConfig({ name: 'my-todos', bindings: { d1: true, // D1 database (always required) kv: true, // KV for sessions }, environments: { production: { vars: { APP_URL: 'https://my-todos.pages.dev', }, }, },});Configure Key Files
Section titled “Configure Key Files”The scaffold generates several files that require a small amount of wiring. Complete these steps before continuing.
src/trpc/client.ts
Section titled “src/trpc/client.ts”Replace the generated file with this version that sends the auth token on every request:
import { createTRPCReact } from '@trpc/react-query';import { createTRPCClientFactory, createDefaultQueryClient } from '@cruzjs/core/trpc/client';import type { AppRouter } from './router';
export const trpc = createTRPCReact<AppRouter>();
export function createTRPCClient() { return createTRPCClientFactory(trpc);}
export function createQueryClient() { return createDefaultQueryClient();}createTRPCClientFactory reads the session token from localStorage and adds an Authorization: Bearer header to every tRPC request. Without this, all protected procedures return 401.
src/root.tsx
Section titled “src/root.tsx”Add two registration calls at the top of the file so CruzJS framework components can access your tRPC instance:
import { CruzProviders } from '@cruzjs/core/framework/components';import { registerTRPC } from '@cruzjs/core/trpc/client';import { registerOrgTRPC } from '@cruzjs/start/orgs/org.hooks';import { trpc, createTRPCClient, createQueryClient } from '@/trpc/client';import { theme } from '@/theme';
// Register tRPC so @cruzjs/* package components can access the hooksregisterTRPC(trpc);registerOrgTRPC(trpc);
// ... rest of root.tsxvite.config.ts
Section titled “vite.config.ts”Add @cruzjs/core, @cruzjs/start, and @cruzjs/pro to optimizeDeps.exclude. Without this, Vite pre-bundles these packages as separate module copies, so registerTRPC() sets state in one copy while framework components read from another:
optimizeDeps: { exclude: ['inversify', '@cruzjs/core', '@cruzjs/start', '@cruzjs/pro'],},Check Auth Works
Section titled “Check Auth Works”The scaffold includes a working registration and sign-in flow. Visit http://localhost:5000/auth/register and create an account.
You should be redirected to an org setup screen and then the dashboard. This confirms the auth stack and database are working correctly.