Skip to main content

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: true fields 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

📖 Complete Migration Guide →

Next Steps