Generated Code Overview
Radish CLI generates a complete, production-ready data layer from your blueprint files.
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
├── server/
│ ├── models/ # MongoDB/Mongoose models
│ ├── repos/ # Data access layer
│ ├── services/ # Business logic
│ ├── routes/ # HTTP API routes
│ │ └── api/v1/ # Versioned API endpoints
│ ├── auth/ # Authentication adapters
│ └── seeds/ # Database seeders
└── 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
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/server/models/
Mongoose models with proper schemas, indexes, and virtuals:
// .radish/lib/datalayer/server/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/server/repos/
CRUD operations with filtering, population, and pagination:
// .radish/lib/datalayer/server/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/server/services/
Permission-checked business logic with validation:
// .radish/lib/datalayer/server/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/server/routes/api/v1/
SvelteKit-compatible route handlers:
server/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 { getEntityMetadata } from '@generated/datalayer/metadata';
const metadata = getEntityMetadata('Product');
// Returns: { name, plural, fields, filters, permissions, versioning, ... }
7. 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.yml",
"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.yml",
"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": "server/models/" },
"repositories": { "files": 28, "path": "server/repos/" },
"services": { "files": 32, "path": "server/services/" },
"routes": { "files": 156, "path": "server/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.yml
* 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",
"./server/*": "./server/*"
},
"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 '@generated/datalayer/server/repos/ (internal - use services instead)product.repo';
const repo = new ProductRepo();
const products = await repo.list({ status: 'active' });
Metadata
import { getEntityMetadata, getEntityList } from '@generated/datalayer/metadata';
const allEntities = getEntityList();
const productMeta = getEntityMetadata('Product');
Regeneration
To regenerate code after blueprint changes:
radish-cli create datalayer . --schema blueprints/app.types.yml
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
Check if generated code matches blueprint:
# View manifest info
cat .radish/MANIFEST.json | jq '.blueprints.types.hash'
# Compare blueprint hash
cat .radish/lib/datalayer/MANIFEST.json | jq '.blueprint.hash'
Future CLI commands (planned):
radish-cli verify # Check if code matches blueprint
radish-cli info # Display manifest information
radish-cli compare <env> # Compare manifests across environments
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
- Services - Service layer details
- Repositories - Data access layer
- Metadata - Runtime introspection