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
reffields 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/paginationPOST /api/v1/products- Create new recordGET /api/v1/products/:id- Get by ID with populationPATCH /api/v1/products/:id- Update recordDELETE /api/v1/products/:id- Delete recordGET /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/healthreporting service status
See Telemetry for full documentation.
12. Idempotency
Always on. Every generated app includes request idempotency protection:
- Clients send an
Idempotency-Keyheader 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:
- Blueprint validation — validates all blueprint files against their JSON schemas
- Drift detection — compares blueprint hashes against manifest
- Orphan scan — finds generated files with no corresponding blueprint entity
- 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:
- Extend services - Wrap generated services with custom logic
- Add custom routes - Create routes outside
.radish/directory - Extend entities - Use blueprint extension syntax
- 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;
}
}
Related Documentation
- Getting Started - Quick start guide
- Blueprint Reference - Blueprint syntax
- CLI Reference - Command-line options
- Builtin Entities - All 24–25 system entities by category
- Services - Service layer details
- Repositories - Data access layer
- Metadata - Runtime introspection
- Scheduled Jobs - Background task scheduling and execution
- Job Snapshots - Declared metrics from scheduled jobs
- Versioning & Audit Logs - Change tracking and revert
- Telemetry - Observability infrastructure
- Search - Full-text search with Typesense
- Datasets - Analytics aggregation from remote sources
- Access Control - Four-layer authorization system
- Auth - Authentication and session management
- Settings - Key-value configuration with encryption