Blueprint System Overview
Radish CLI uses a comprehensive blueprint system to define your application. Blueprints are YAML files that describe your app's structure, behavior, and data model, enabling complete code generation from declarative specifications.
What Are Blueprints?
Blueprints are declarative configuration files that describe:
- What your application should do
- How it should behave
- Who can access what
The CLI reads these blueprints and generates production-ready TypeScript code, including:
- Type-safe contracts (Zod schemas + TypeScript types)
- Database models with automatic indexing
- Business logic services with authorization
- REST API endpoints
- Authentication and permission systems
Blueprint Types
Radish currently supports two blueprint types:
🗄️ Types Blueprint ({app}.types.yml)
Defines your data model - entities, fields, relationships, and business rules.
version: 1
defaults:
timestamps: true # Auto-add createdAt/updatedAt
owned: true # Auto-add ownerId
entities:
Book:
label: Book
plural: books
fields:
title: { type: string, required: true, search: true }
isbn: { type: string, unique: true, optional: true }
authorId: { type: objectId, ref: Author, required: true }
status: { type: enum, values: [draft, published], default: draft }
filters: [authorId, status] # Auto-generates indexes
indexes:
- fields: [authorId, status] # Manual compound index
Generates:
- TypeScript contracts with Schema suffix naming (
bookSchema,createBookSchema) - Database models with automatic indexes
- Repository classes with CRUD operations
- Service classes with business logic and authorization
- REST API endpoints with populate support
- Permission registry
📖 Complete Types Blueprint Documentation →
🛡️ Roles Blueprint ({app}.roles.yml)
Defines roles and permissions for authentication and authorization.
version: 1
roles:
LIBRARIAN:
label: "Librarian"
description: "Can manage all books and authors"
isSystem: false
permissions:
- "book:view:all"
- "book:edit:all"
- "author:view:all"
- "author:edit:all"
Generates:
- Role entities and services
- Permission registry with metadata
- Authentication middleware
- Authorization guards and helpers
- API key management system
- User management endpoints
📖 Complete Roles Blueprint Documentation →
File Organization
my-app/
├── blueprints/
│ ├── my-app.types.yml # Data model
│ └── my-app.roles.yml # Authentication & permissions
└── .radish/
└── lib/
└── datalayer/
├── contracts/ # Generated Zod schemas & types
│ ├── common.ts
│ ├── book.ts
│ ├── author.ts
│ └── metadata/
├── server/
│ ├── models/ # Mongoose models
│ ├── repos/ # Repository classes
│ ├── services/ # Business logic services
│ ├── auth/ # Authentication & guards
│ ├── http/ # HTTP adapters & handlers
│ └── routes/ # REST API endpoints
└── docs/ # Generated API docs
Blueprint Philosophy
Declarative Over Imperative
Describe what you want, not how to build it. The generator handles implementation details.
Convention Over Configuration
Smart defaults reduce boilerplate. Override only when needed.
Type Safety First
All generated code is fully typed with TypeScript. Runtime validation with Zod schemas.
Schema Suffix Naming
Clear distinction between runtime validators and compile-time types:
- Schemas:
bookSchema,createBookSchema(camelCase + Schema) - Types:
Book,CreateBook(PascalCase)
Progressive Enhancement
Start minimal and add features as needed. Every feature is opt-in.
Version Control Friendly
Human-readable YAML that diffs well. Generated code is organized and predictable.
Working with Blueprints
Creating Your First Blueprint
1. Create the types blueprint:
mkdir -p blueprints
touch blueprints/library.types.yml
version: 1
defaults:
timestamps: true
owned: true
entities:
Book:
plural: books
fields:
title: { type: string, required: true, search: true }
isbn: { type: string, unique: true }
authorId: { type: objectId, ref: Author, required: true }
2. Create the roles blueprint:
touch blueprints/library.roles.yml
version: 1
roles:
LIBRARIAN:
label: "Librarian"
permissions:
- "book:view:all"
- "book:edit:all"
3. Generate the application:
radish-cli create datalayer . --schema blueprints/library.types.yml
This generates all code in .radish/lib/datalayer/.
Regenerating After Changes
Whenever you modify a blueprint, regenerate:
radish-cli create datalayer . --schema blueprints/library.types.yml
The generator overwrites all files in .radish/lib/datalayer/ with fresh code based on your updated blueprints.
Blueprint Validation
Each blueprint type has JSON schema validation that runs automatically during generation:
❌ Blueprint validation failed:
[
{
"instancePath": "/entities/Book/fields/invalidField",
"message": "must have required property 'type'"
}
]
Common validation errors:
- Missing required properties (
type,plural, etc.) - Invalid field types
- Circular entity references
- Invalid permission patterns
Cross-Blueprint References
Blueprints work together to create cohesive applications:
# In types blueprint - defines entity
Book:
fields:
title: { type: string, required: true }
# In roles blueprint - references entity for permissions
LIBRARIAN:
permissions:
- "book:view:all" # References Book entity
- "book:edit:all"
Generated Code Structure
Contracts Package
Located in .radish/lib/datalayer/contracts/:
// Import schemas and types
import { bookSchema, createBookSchema } from '@generated/datalayer/contracts';
import type { Book, CreateBook } from '@generated/datalayer/contracts';
// Runtime validation
const book = bookSchema.parse(data);
// Type annotations
function processBook(book: Book): void { ... }
📖 Complete Contracts Documentation →
Server Package
Located in .radish/lib/datalayer/server/:
// Import services
import { BookService } from '@generated/datalayer/services';
import type { AuthContext } from '@generated/server/auth';
// Use in your application
const ctx: AuthContext = { userId, roles, permissions };
const service = new BookService(ctx);
const books = await service.list({ limit: 20 });
📖 Complete Services Documentation →
Common Patterns
Entity Relationships
Book:
fields:
authorId: { type: objectId, ref: Author, required: true }
categoryId: { type: objectId, ref: Category }
reviewerIds: { type: array, items: { type: objectId, ref: User } }
Generated features:
- Automatic indexing on reference fields
- Populate support via
?populate=authorId,categoryId - Type-safe references in TypeScript
Search and Filtering
Book:
fields:
title: { type: string, search: true }
author: { type: string, search: true }
status: { type: enum, values: [draft, published] }
filters: [status, categoryId]
Generated features:
- Full-text search on
search: truefields via?q=search+term - Filterable fields via query parameters:
?status=published - Automatic indexes on filter fields
Timestamps and Ownership
defaults:
timestamps: true # Adds createdAt, updatedAt
owned: true # Adds ownerId
Book:
# Automatically includes: ownerId, createdAt, updatedAt
fields:
title: { type: string, required: true }
Versioning
Book:
versioning: full # or 'simple'
fields:
title: { type: string, required: true }
price:
type: float
trackChanges: true # Enhanced field tracking
Generated features:
- Version history endpoints
- Audit logs
- Revert capability (full versioning only)
- Field-level change tracking with approvals
📖 Complete Versioning Documentation →
Best Practices
1. Use Meaningful Names
# ✅ Good
Book:
plural: books
fields:
title: { type: string }
publishedAt: { type: isoDate }
# ❌ Bad
Bk:
plural: bks
fields:
t: { type: string }
d: { type: isoDate }
2. Define Relationships Consistently
# ✅ Good - Uses ref for type safety
Book:
fields:
authorId: { type: objectId, ref: Author }
# ❌ Bad - Just a string, no validation
Book:
fields:
authorId: { type: string }
3. Use Filters for Common Queries
# ✅ Good - Auto-indexed for performance
Book:
filters: [status, categoryId, publishedAt]
# ❌ Bad - Will scan entire collection
Book:
fields:
status: { type: enum, values: [...] }
# Missing filters definition
4. Document with Labels and Descriptions
Book:
label: "Book"
description: "Books in the library catalog"
fields:
isbn:
type: string
label: "ISBN"
description: "International Standard Book Number"
helpText: "Format: XXX-X-XXX-XXXXX-X"
5. Version Your Blueprints
Always specify version: 1 at the top of your blueprints. This enables future migration paths when blueprint schemas evolve.
Troubleshooting
Blueprint validation failed
Cause: Invalid YAML syntax or schema violations.
Solution:
- Check YAML syntax (indentation, colons, quotes)
- Verify all required properties are present
- Check error message for specific field causing issue
Cannot find referenced entity
Cause: Entity referenced in ref doesn't exist.
Solution:
- Ensure referenced entity is defined in same blueprint
- Check spelling of entity name (case-sensitive)
- Define entities before referencing them
Generated code has TypeScript errors
Cause: Blueprint changes not fully regenerated.
Solution:
# Delete generated code
rm -rf .radish/lib/datalayer
# Regenerate fresh
radish-cli create datalayer . --schema blueprints/app.types.yml
# Install dependencies
cd .radish/lib/datalayer && npm install
Migration & Versioning
Adding New Entities
Safe - just regenerate:
radish-cli create datalayer . --schema blueprints/app.types.yml
Modifying Existing Entities
Safe changes (no migration needed):
- Adding optional fields
- Adding new entities
- Changing labels/descriptions
- Adding indexes
Breaking changes (migration required):
- Adding required fields
- Changing field types
- Removing fields
- Renaming fields
Next Steps
- Entity Definitions - Complete entity and field reference
- Field Types - All available field types
- Blueprint Examples - Real-world blueprint patterns
- Roles & Permissions - Authentication system
- Generated Code - Understanding generated structure