Skip to main content

Generated Code Overview

Radish CLI generates a complete, production-ready data layer from your blueprint files.

Radish supports two target stacks:

  • Node/SvelteKit (default) — MongoDB models (Mongoose), Zod schemas, TypeScript contracts, SvelteKit API routes, and a Svelte admin panel.
  • Laravel/PHP (--stack laravel) — Eloquent models, database migrations, Form Requests, API resource controllers, service classes, and Sanctum auth with policies.

Both stacks share the same blueprint format and generate the same feature set: RBAC, idempotency, rate limiting, observability, and all builtin entities. The directory structure and code examples below show the Node/SvelteKit stack. Use radish-cli create datalayer <dir> --stack laravel to generate the PHP variant.

What Gets Generated​

Directory Structure​

.radish/
├── MANIFEST.json # Top-level generation metadata
├── README.md # Generated code guide
├── docs/ # Radish CLI documentation
│ ├── INDEX.md
│ ├── getting-started.md
│ ├── blueprints/
│ ├── features/
│ └── ...
└── lib/
└── datalayer/
├── MANIFEST.json # Datalayer-specific metadata
├── package.json # @generated/datalayer package
├── contracts/ # Zod schemas & TypeScript types
│ ├── index.ts # All exports
│ ├── common.ts # Base schemas
│ ├── product.ts # Entity schemas
│ └── metadata/ # Field metadata
├── core/ # Core infrastructure
│ ├── auth/ # Authentication adapters
│ ├── db/ # Database connection
│ ├── hooks/ # Lifecycle hooks
│ ├── http/ # HTTP adapters
│ ├── lib/ # Shared libraries
│ ├── middleware/ # Middleware
│ ├── models/ # MongoDB/Mongoose models
│ ├── repos/ # Data access layer
│ ├── seeds/ # Database seeders
│ └── util/ # Utilities
├── docs/ # App-specific API docs
│ ├── API.md # Complete API reference
│ ├── services.md # Service layer docs
│ ├── types.md # Type definitions
│ ├── roles.md # Auth & permissions
│ ├── AI_CONTEXT.md # AI-friendly summary
│ └── openapi.json # OpenAPI 3.0 spec
├── routes/ # HTTP API routes
│ └── api/v1/ # Versioned API endpoints
└── services/ # Business logic

Generated Components​

1. Contracts (TypeScript Types & Zod Schemas)​

Location: .radish/lib/datalayer/contracts/

Radish generates type-safe contracts for every entity:

// .radish/lib/datalayer/contracts/product.ts

// Zod schema for validation
export const productSchema = z.object({
id: z.string(),
title: z.string(),
price: z.number(),
// ... more fields
});

// TypeScript type inferred from schema
export type Product = z.infer<typeof productSchema>;

// Create/update schemas (subset of fields)
export const createProductSchema = productSchema.omit({ id: true });
export type CreateProduct = z.infer<typeof createProductSchema>;

Import in your code:

import type { Product, CreateProduct } from '@generated/datalayer/contracts/product';
import { productSchema, createProductSchema } from '@generated/datalayer/contracts/product';

// Validate data
const result = productSchema.safeParse(data);
if (result.success) {
const product: Product = result.data;
}

2. Models (Database Layer)​

Location: .radish/lib/datalayer/core/models/

Mongoose models with proper schemas and indexes:

// .radish/lib/datalayer/core/models/product.model.ts
export const ProductModel = mongoose.model('Product', productSchema);

Features:

  • Automatic indexes on ref fields and filters
  • Compound indexes from blueprint
  • Timestamps (createdAt, updatedAt)
  • Owner scoping support
  • Versioning hooks (if enabled)

3. Repositories (Data Access Layer)​

Location: .radish/lib/datalayer/core/repos/

CRUD operations with filtering, population, and pagination:

// .radish/lib/datalayer/core/repos/product.repo.ts
export class ProductRepo extends BaseRepo<Product> {
async findById(id: string): Promise<Product | null>
async list(criteria: Criteria, options?: QueryOptions): Promise<Product[]>
async create(data: Partial<Product>): Promise<Product>
async update(id: string, data: Partial<Product>): Promise<Product>
async delete(id: string): Promise<boolean>
// ... more methods
}

4. Services (Business Logic Layer)​

Location: .radish/lib/datalayer/services/

Permission-checked business logic with validation:

// .radish/lib/datalayer/services/product.service.ts
export class ProductService {
static async create(ctx: AuthContext, input: CreateProduct): Promise<Product>
static async getById(ctx: AuthContext, id: string, populate?: PopulateOptions): Promise<Product>
static async list(ctx: AuthContext, criteria: Criteria, options?: QueryOptions): Promise<Product[]>
static async update(ctx: AuthContext, id: string, input: UpdateProduct): Promise<Product>
static async delete(ctx: AuthContext, id: string): Promise<boolean>
// ... more methods
}

Features:

  • Permission checking (admin, user, owner-scoped)
  • Input validation with Zod schemas
  • Error handling with standardized codes
  • Reference population support
  • Versioning/audit trail integration

5. Routes (HTTP API Layer)​

Location: .radish/lib/datalayer/routes/api/v1/

SvelteKit-compatible route handlers:

routes/api/v1/
├── products/
│ ├── +server.ts # GET /api/v1/products, POST /api/v1/products
│ └── [id]/
│ ├── +server.ts # GET/PATCH/DELETE /api/v1/products/:id
│ └── versions/
│ └── +server.ts # GET /api/v1/products/:id/versions (if versioning enabled)
└── ...

Generated endpoints:

  • GET /api/v1/products - List with filtering/pagination
  • POST /api/v1/products - Create new record
  • GET /api/v1/products/:id - Get by ID with population
  • PATCH /api/v1/products/:id - Update record
  • DELETE /api/v1/products/:id - Delete record
  • GET /api/v1/products/:id/versions - Version history (if enabled)

6. Metadata APIs​

Location: .radish/lib/datalayer/contracts/metadata/

Runtime schema introspection for dynamic UIs:

import { getEntityNames, getEntityMetadata, getAllEntitiesMetadata } from '@generated/datalayer/metadata';

const names = getEntityNames(); // ['Product', 'User', ...]
const metadata = getEntityMetadata('Product');
// Returns: { entity: { label, description, plural, ownership, filters, searchFields, indexes, entityId }, fields: {...} }

const all = getAllEntitiesMetadata(); // All entities with flattened metadata

7. Search Infrastructure​

Location: .radish/lib/datalayer/search/

When entities declare search.enabled: true, Radish generates a complete search stack:

search/
├── indexes/
│ └── product.index.ts # Index schema, toSearchDoc() mapper
├── adapters/
│ ├── base.adapter.ts # Abstract search adapter interface
│ └── typesense.adapter.ts # Typesense client adapter
└── index.ts # Barrel exports

Plus a search API endpoint at routes/api/v1/search/+server.ts.

Usage:

import { ProductSearchIndex } from '@generated/datalayer/search';

// Index a document
const doc = ProductSearchIndex.toSearchDoc(product);
await searchAdapter.index('products', doc);

// Search via API
// GET /api/v1/search?q=coffee&entity=Product&limit=20

Search also generates builtin feedback entities (SearchQuery, SearchClick, SearchAssociation) for relevance learning. See Search for full documentation.

8. Datasets (Analytics Aggregation)​

Enabled by: features.datasets: true

When datasets is enabled, Radish generates entities and services for syncing analytics data from remote sources (Databricks, MongoDB, HTTP) and querying rollups, trends, and rankings:

services/
├── dataset.service.ts # Read path: rollup(), trend(), topN()
└── dataset-sync.service.ts # Write path: source-agnostic sync engine

Plus three entities (DatasetDefinition, DatasetField, DatasetRow) and analytics API endpoints. See Datasets for full documentation.

9. Scheduled Jobs​

Always on. Every generated app includes background job infrastructure:

services/
├── scheduledjob.service.ts # Job CRUD + scheduling
├── jobexecution.service.ts # Execution tracking
└── job-snapshot.service.ts # Metric snapshots (series, rollup)

jobs/
└── snapshot-buffer.ts # Declared snapshot validation + buffering

Register handlers with cron or interval schedules, track execution history, and optionally capture declared metric snapshots. The admin panel includes a dedicated Jobs page with enable/disable, manual trigger, and execution history.

See Scheduled Jobs for handler registration and the scheduling lifecycle. See Job Snapshots for the declared metrics system.

10. Versioning & Audit Logs​

Enabled by: versioning: "full" or versioning: "simple" on an entity, or trackChanges: true on individual fields.

Radish generates three levels of change tracking:

  • Full versioning — complete entity snapshots with revert capability
  • Simple versioning — lightweight audit logs with field-level diffs
  • Field-level tracking — enhanced metadata for critical fields with approval workflows
core/models/
├── entity-version.model.ts # Full snapshot history
├── entity-auditlog.model.ts # Audit trail entries
└── entity-fieldchange.model.ts # Per-field change tracking

contracts/
├── entity-version.ts # Version schemas
├── entity-auditlog.ts # Audit log schemas
└── entity-fieldchange.ts # Field change schemas

Versioned entities get additional API endpoints (/versions, /revert) and the admin detail page shows version history. See Versioning & Audit Logs for full documentation.

11. Telemetry & Observability​

Always on. Every generated app includes production observability infrastructure — no configuration needed:

  • Correlation IDs — unique request ID propagated through logs and API responses (X-Request-Id)
  • Service instrumentation — automatic timing and success/failure tracking for every service method
  • Structured logger — JSON-formatted logs with correlation context, log levels, and source tagging
  • Health endpoint — GET /api/v1/health reporting service status

See Telemetry for full documentation.

12. Idempotency​

Always on. Every generated app includes request idempotency protection:

  • Clients send an Idempotency-Key header with a UUID
  • The middleware stores the key, a SHA-256 hash of the request body, and the cached response
  • Duplicate requests return the cached response instead of re-processing
  • Mismatched body on the same key is rejected (prevents accidental replay with different data)

13. Builtin Entities​

Radish ships 24–25 builtin entities across 9 categories (Auth, Settings, Jobs, Logging, Idempotency, Search, CMS, Grants, Datasets). Some are always-on, some are auto-detected from blueprint features, and some require explicit --features flags.

See Builtin Entities for the complete reference.

14. Documentation​

Location: .radish/lib/datalayer/docs/

Auto-generated documentation:

  • API.md - Complete API endpoint reference
  • services.md - Service layer documentation
  • types.md - TypeScript type definitions
  • roles.md - Authentication and permissions
  • openapi.json - OpenAPI 3.0 specification
  • AI_CONTEXT.md - AI-friendly architecture summary

Manifest Files​

Radish generates manifest files to track generation metadata and enable verification.

Top-Level Manifest​

Location: .radish/MANIFEST.json

Tracks overall generation state:

{
"$schema": "https://radish-cli.dev/schemas/manifest.v1.json",
"manifestVersion": "1.0",
"radishVersion": "0.6.0",
"projectName": "my-app",
"generatedAt": "2026-03-11T10:30:00.000Z",

"blueprints": {
"types": {
"path": "blueprints/app.types.json",
"hash": "sha256:89f21a4b2c3d4e5f",
"lastModified": "2026-03-11T09:15:00.000Z"
}
},

"layers": {
"datalayer": {
"enabled": true,
"manifestPath": "lib/datalayer/MANIFEST.json",
"version": "1.0.0",
"generatedAt": "2026-03-11T10:30:00.000Z"
}
},

"cli": {
"version": "0.6.0",
"command": "radish-cli create datalayer .",
"cwd": "/path/to/project",
"nodeVersion": "v20.10.0",
"platform": "darwin"
}
}

Use cases:

  • Blueprint verification - Check if generated code matches current blueprint
  • Debugging - Know exact CLI version and generation timestamp
  • CI/CD validation - Fail builds if generated code is stale
  • Environment sync - Compare staging vs production manifests

Datalayer Manifest​

Location: .radish/lib/datalayer/MANIFEST.json

Detailed datalayer metadata:

{
"$schema": "https://radish-cli.dev/schemas/datalayer-manifest.v1.json",
"manifestVersion": "1.0",
"layer": "datalayer",
"radishVersion": "0.6.0",
"generatedAt": "2026-03-11T10:30:00.000Z",

"blueprint": {
"path": "blueprints/app.types.json",
"hash": "sha256:89f21a4b2c3d4e5f",
"entities": 12,
"builtinEntities": 3,
"customEntities": 9
},

"entities": [
{
"name": "Product",
"type": "custom",
"builtin": false,
"fields": 8,
"relationships": ["Category", "Vendor"],
"features": {
"versioning": "full",
"filtering": true,
"population": true,
"permissions": ["admin", "user"]
}
}
],

"generated": {
"contracts": { "files": 45, "path": "contracts/" },
"models": { "files": 28, "path": "core/models/" },
"repositories": { "files": 28, "path": "core/repos/" },
"services": { "files": 32, "path": "services/" },
"routes": { "files": 156, "path": "routes/" },
"metadata": { "files": 29, "path": "contracts/metadata/" },
"docs": { "files": 8, "path": "docs/" }
},

"database": {
"type": "mongodb",
"adapter": "mongoose"
},

"permissions": {
"roles": ["admin", "user", "anonymous"],
"wildcards": true
}
}

Benefits:

  • Entity overview - See all entities, field counts, relationships at a glance
  • File tracking - Know how many files were generated per category
  • Feature detection - Identify which entities have versioning, permissions, etc.
  • Change detection - Blueprint hash changes when blueprint is modified

File Headers​

All generated files include headers with generation metadata:

/**
* ═══════════════════════════════════════════════════════════
* 🌱 RADISH CLI - AUTO-GENERATED CODE
* ═══════════════════════════════════════════════════════════
*
* Generator: Radish CLI v0.6.0
* Blueprint: blueprints/app.types.json
* Entity: Product
* Package: @generated/datalayer
*
* ⚠️ DO NOT EDIT
* Changes will be overwritten on next generation.
*
* Regenerate: radish-cli create datalayer .
* Docs: https://radish-cli.dev
* ═══════════════════════════════════════════════════════════
*/

Note: File headers DO NOT include blueprint hashes (to avoid git churn). Blueprint hashes are tracked in manifest files instead.

Package Structure​

Generated code is packaged as @generated/datalayer with its own dependencies:

.radish/lib/datalayer/package.json:

{
"name": "@generated/datalayer",
"version": "0.6.0",
"private": true,
"type": "module",
"exports": {
"./contracts": "./contracts/index.ts",
"./services": "./services/index.ts",
"./metadata": "./contracts/metadata/index.ts"
},
"dependencies": {
"zod": "^3.22.0",
"mongoose": "^8.0.0",
"mongodb": "^6.3.0",
"bcrypt": "^5.1.1"
}
}

Benefits:

  • Self-contained dependencies (no pollution of parent package.json)
  • Can be published to npm or local registry
  • Easier to version and track
  • Import with @generated/* alias

Import Patterns​

TypeScript Types​

import type { Product, CreateProduct, UpdateProduct } from '@generated/datalayer/contracts/product';
import type { User, Profile } from '@generated/datalayer/contracts';

Zod Schemas (for validation)​

import { productSchema, createProductSchema } from '@generated/datalayer/contracts/product';

const result = createProductSchema.safeParse(input);

Services​

import { ProductService } from '@generated/datalayer/services';

const product = await ProductService.create(ctx, data);

Repositories (if needed directly)​

import { ProductRepo } from '@core/repos/product.repo'; // internal — prefer services for app code

const repo = new ProductRepo();
const products = await repo.list({ status: 'active' });

Metadata​

import { getEntityNames, getEntityMetadata, getAllEntitiesMetadata } from '@generated/datalayer/metadata';

const allEntities = getEntityNames();
const productMeta = getEntityMetadata('Product');

Regeneration​

To regenerate code after blueprint changes:

radish-cli create datalayer . --schema blueprints/app.types.json

What happens:

  • All generated files are overwritten
  • Manifest files are updated with new blueprint hash
  • File counts and entity metadata refreshed
  • Package dependencies reinstalled

What's preserved:

  • Your custom code (not in .radish/ directory)
  • Database data (schemas may evolve, data remains)
  • Custom routes/services (if not in generated directories)

Verification​

Radish tracks blueprint hashes and generation state so you can detect when generated code is stale, when blueprints have drifted from what was last generated, or when orphan files remain from deleted entities.

CLI Commands​

radish-cli verify [app-path]    # Check blueprint hashes + detect orphan files
radish-cli doctor [app-path] # Comprehensive health check (validation, drift, orphans, search)
radish-cli info [app-path] # Display manifest information
radish-cli validate [app-path] # Validate blueprints against JSON schemas

verify — Hash Comparison + Orphan Detection​

Compares the current blueprint file hash against the hash recorded in the manifest at generation time:

$ radish-cli verify .
✅ Blueprint hash matches — generated code is current
🔍 Checking for orphan files...
✅ No orphan files detected

When the blueprint has changed since the last generation:

$ radish-cli verify .
⚠️ Blueprint hash mismatch — generated code is stale
Blueprint: sha256:a1b2c3d4...
Generated: sha256:e5f6a7b8...
Run: radish-cli create datalayer . --schema blueprints/app.types.json

🔍 Checking for orphan files...
⚠️ Orphan files detected (entity removed from blueprint but files remain):
- .radish/lib/datalayer/contracts/oldentity.ts
- .radish/lib/datalayer/core/models/oldentity.model.ts
- .radish/lib/datalayer/services/oldentity.service.ts

doctor — Comprehensive Health Check​

Runs all verification checks in sequence:

  1. Blueprint validation — validates all blueprint files against their JSON schemas
  2. Drift detection — compares blueprint hashes against manifest
  3. Orphan scan — finds generated files with no corresponding blueprint entity
  4. Search index status — checks search engine health and index sync state (if search is enabled)
$ radish-cli doctor .
🩺 Running health checks...

✅ Blueprints valid
⚠️ Blueprint drift detected — regeneration needed
✅ No orphan files
✅ Search indexes healthy (14/14 entities synced)

1 issue found. Run with --fix for suggestions.

CI/CD Integration​

Use verify in CI to fail builds when generated code is stale:

# .gitlab-ci.yml
verify:
script:
- radish-cli verify . --strict # exits non-zero on drift

Manual Hash Inspection​

For debugging, you can also inspect manifest hashes directly:

# View generation metadata
cat .radish/MANIFEST.json | jq '.blueprints.types.hash'

# Compare against current blueprint
cat .radish/lib/datalayer/MANIFEST.json | jq '.blueprint.hash'

Best Practices​

DO:​

  • ✅ Import from @generated/* paths
  • ✅ Use TypeScript types for type annotations
  • ✅ Use Zod schemas for validation
  • ✅ Check manifest files for debugging
  • ✅ Regenerate after blueprint changes
  • ✅ Review manifest diff in git to understand changes

DON'T:​

  • ❌ Edit generated files directly (will be overwritten)
  • ❌ Copy generated code to customize (extend instead)
  • ❌ Commit .radish/ to git (optional - some teams do)
  • ❌ Manually modify manifest files
  • ❌ Mix old and new import paths

Customization​

To customize generated behavior:

  1. Extend services - Wrap generated services with custom logic
  2. Add custom routes - Create routes outside .radish/ directory
  3. Extend entities - Use blueprint extension syntax
  4. Override specific methods - Subclass generated repos/services

Example: Custom service wrapper

// src/services/product.service.ts
import { ProductService as GeneratedProductService } from '@generated/datalayer/services';

export class ProductService extends GeneratedProductService {
static async create(ctx, input) {
// Custom validation
if (input.price < 0) {
throw new Error('Price cannot be negative');
}

// Call generated method
const product = await super.create(ctx, input);

// Custom post-processing
await sendNotification('New product created', product);

return product;
}
}