Skip to main content

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 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/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/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 { 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:

  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;
}
}