Skip to content

Directory Structure

When you scaffold a project with npx create-cruz-app my-app, you get a flat project structure (not a monorepo). The @cruzjs/core, @cruzjs/start, and @cruzjs/pro packages are installed as regular npm dependencies from node_modules.

my-app/
├── src/
│ ├── entry.server.tsx # SSR entry point — handles each request
│ ├── entry.client.tsx # Client-side hydration entry
│ ├── root.tsx # Root React component with providers
│ ├── routes.ts # React Router route config
│ ├── server.cloudflare.ts # App bootstrap (createCruzApp)
│ │
│ ├── database/
│ │ ├── schema.ts # Central schema (re-exports from packages + your tables)
│ │ └── migrations/ # Generated Drizzle migrations
│ │
│ ├── features/ # Your feature modules
│ │ └── <feature-name>/
│ │ ├── index.ts # Barrel exports
│ │ ├── <feature>.module.ts # @Module (providers, trpcRouters, events)
│ │ ├── <feature>.router.ts # tRPC router
│ │ ├── <feature>.service.ts # Business logic (@Injectable)
│ │ ├── <feature>.schema.ts # Drizzle table definition
│ │ ├── <feature>.validation.ts # Zod input schemas
│ │ ├── <feature>.models.ts # TypeScript types
│ │ ├── routes/ # Feature-specific React Router routes
│ │ │ ├── index.tsx
│ │ │ └── $id.tsx
│ │ └── events/ # Domain events (optional)
│ │
│ ├── components/ # Shared React components
│ ├── contexts/ # React context providers
│ └── trpc/
│ ├── client.ts # tRPC React client hooks
│ └── router.ts # Combined AppRouter (all feature routers)
├── external-processes/ # Standalone Workers, Workflows, Queues
│ └── <name>/ # Each with its own wrangler.toml
├── public/ # Static assets served directly
├── cruz.config.ts # CruzJS deployment configuration
├── wrangler.toml # Generated Cloudflare config (do not edit manually)
├── vite.config.ts # Vite build configuration
├── tsconfig.json # TypeScript configuration
├── package.json # Has @cruzjs/core, @cruzjs/start, @cruzjs/pro as dependencies
└── .env # Local environment variables

Defines your app name, Cloudflare bindings (D1, KV, R2, AI), shared variables, and per-environment settings. The CLI reads this to generate wrangler.toml and provision infrastructure.

The server entry point that bootstraps the application via createCruzApp(). Sets the database schema, registers modules, configures the runtime adapter, and exports the server handler.

The SSR entry point for every request. Initializes CloudflareContext from the React Router load context (extracting D1, KV, R2 bindings), then renders the app to a ReadableStream.

The single source of truth for all database tables. Re-exports tables from @cruzjs/core, @cruzjs/start, and @cruzjs/pro, plus any app-specific tables you define in your features.

Combines all tRPC routers (from core, pro, and your features) into a single AppRouter. This type is exported and used by the tRPC client for end-to-end type safety.

React Router route configuration. Routes point to files inside feature folders. For example, a forum feature’s detail page at src/features/forum/routes/$id.tsx would be mapped to /forums/:id in this file.

Each feature in src/features/ is a self-contained module. Routes live inside the feature folder, keeping all related code together.

FilePurpose
index.tsBarrel exports for the feature
<name>.module.ts@Module decorator — declares providers, trpcRouters, pageRoutes, and event listeners
<name>.router.tstRPC router — defines API endpoints (queries and mutations)
<name>.service.tsBusiness logic — @Injectable() class with database operations
<name>.schema.tsDrizzle table definition — columns, indexes, foreign keys
<name>.validation.tsZod schemas — input validation for tRPC procedures
<name>.models.tsTypeScript types and interfaces
routes/React Router route components for this feature
events/Domain events and listeners (optional)

Features are registered by adding their module to the modules array in createCruzApp().

The framework is distributed as npm packages. You do not have a packages/ folder in your project — these are installed into node_modules like any other dependency.

PackageDescription
@cruzjs/coreFramework runtime — DI, auth, tRPC, database, Cloudflare bindings
@cruzjs/startUI components, theming, starter templates, pre-built auth pages, organizations, roles, permissions
@cruzjs/proBilling, admin dashboard, audit logging
@cruzjs/cliDevelopment and deployment CLI (installed as a dev dependency)

Your package.json lists them as regular dependencies:

{
"dependencies": {
"@cruzjs/core": "^1.0.0",
"@cruzjs/start": "^1.0.0",
"@cruzjs/pro": "^1.0.0"
},
"devDependencies": {
"@cruzjs/cli": "^1.0.0"
}
}
  • CloudflareContext — access D1, KV, R2, AI, Queues
  • DrizzleService — database initialization and schema management
  • ConfigService — typed environment variable access
  • @Injectable(), @Inject(), @Module() — dependency injection decorators
  • router, publicProcedure, protectedProcedure, orgProcedure — tRPC building blocks
  • getAppContainer() — resolve services from the DI container in routers
  • orgProcedure — tRPC procedure with organization context (ctx.org.orgId, ctx.org.role)
  • requirePermission() — role-based permission checks
  • OrgService, MemberService, BillingService — injectable services
  • Organization, member, invitation, and subscription schemas

external-processes/ — Standalone Workers

Section titled “external-processes/ — Standalone Workers”

For background processing that runs outside your main Pages application. Each directory is a standalone Cloudflare Worker with its own wrangler.toml:

external-processes/
├── email-worker/ # Example: email sending worker
│ ├── src/
│ │ └── index.ts # Worker entry point
│ ├── wrangler.toml # Worker-specific config
│ └── package.json
└── data-pipeline/ # Example: queue consumer
├── src/
│ └── index.ts
├── wrangler.toml
└── package.json

Scaffold new external processes with the CLI:

Terminal window
# Standalone Worker
cruz new worker email-sender
# Durable Workflow (retryable multi-step process)
cruz new workflow onboarding-flow
# Queue consumer Worker
cruz new queue-worker invoice-processor --queue invoices

External processes are automatically deployed alongside your main app when you run cruz deploy.

CruzJS configures TypeScript path aliases for clean imports:

// Framework package imports (from node_modules)
import { CloudflareContext } from '@cruzjs/core/shared/cloudflare/context';
import { ConfigService } from '@cruzjs/core/shared/config/config.service';
import { orgProcedure } from '@cruzjs/core/trpc/context';
import { requirePermission } from '@cruzjs/start/orgs/auth.utils';
// Local project imports (from src/)
import { trpc } from '~/trpc/client';
import * as schema from '~/database/schema';

The ~ (or @/) alias maps to your project’s src/ directory. The @cruzjs/<package> imports resolve to npm packages in node_modules.