Architecture Overview
Understanding how TGraph Backend Generator works internally.
System Architecture
TGraph Backend Generator is built on a modular, composable architecture with clear separation of concerns:
┌─────────────────────────────────────────────┐
│ CLI Interface │
│ (src/bin/cli.ts ▸ CommandLineInterface) │
└──────────────────┬──────────────────────────┘
│
┌───────────┴────────────┐
│ │
┌──────▼──────┐ ┌───────▼────────┐
│ Generators │ │ Parsers │
│ │ │ │
│ ApiGenerator │ │ SchemaParser │
│ Dashboard │◄───────│ FieldParser │
│ DtoGenerator │ │ RelationsParser│
└──────┬───────┘ └────────────────┘
│
│ ┌────────────────┐
│ │ Directive │
│ │ System │
├───────────────►│ │
│ │ Format/Upload/ │
│ │ ReadOnly │
│ └────────────────┘
│
│ ┌────────────────┐
│ │ Updaters │
│ │ │
└───────────────►│ AppModule │
│ Module │
│ DataProvider │
└────────────────┘
Core Components
1. Parsers
Purpose: Extract structured data from Prisma schemas and existing code.
Components:
PrismaSchemaParser- Parses entire schema filePrismaFieldParser- Parses individual field declarationsPrismaRelationsParser- Enriches schema with relation metadataNestAppModuleParser- Parses NestJS module files
Flow:
Prisma Schema → SchemaParser → ParsedSchema
↓
FieldParser → PrismaField[]
↓
RelationsParser → Enhanced Fields
2. Directive System
Purpose: Process field-level hints that control generation behavior.
Components:
BaseFieldDirective- Abstract base classTgFormatDirective- Handles@tg_format()TgUploadDirective- Handles@tg_upload()TgReadOnlyDirective- Handles@tg_readonlyFieldDirectiveManager- Coordinates all directives
Flow:
Field with Doc Comment
↓
FieldDirectiveManager.apply()
↓
Each Directive.applyMatch()
↓
Enhanced PrismaField
3. Generators
Purpose: Create code files from parsed schema data.
Components:
ApiGenerator- Orchestrates backend generationDashboardGenerator- Orchestrates frontend generationDtoGenerator- Generates response DTOsNestControllerGenerator- Generates controllersNestServiceGenerator- Generates servicesNestDtoGenerator- Generates create/update DTOsReactComponentsGenerator- Generates React Admin components
Flow:
ApiGenerator
├─> Parse Schema
├─> Resolve Module Paths
├─> Generate DTOs
├─> Generate Services
├─> Generate Controllers
├─> Update Modules
└─> Update AppModule
4. Updaters
Purpose: Safely modify existing code files while preserving manual content.
Components:
NestAppModuleUpdater- Updates app.module.tsNestModuleUpdater- Updates individual module filesDataProviderEndpointGenerator- Updates data provider
Strategy:
- Use sentinel comments to mark auto-generated sections
- Parse existing code before modifying
- Preserve manual code outside sentinel comments
- Format generated code for consistency
5. Utilities
Purpose: Provide shared functionality across components.
Components:
ModulePathResolver- Locates module directories- Naming utilities - Consistent name transformations
- File system utilities - Safe file operations
- Formatting utilities - Code formatting via Prettier
Generation Pipeline
API Generation Pipeline
1. Parse Schema
├─> Read prisma/schema.prisma
├─> Extract models with @tg_form()
├─> Parse fields with directives
└─> Build PrismaModel[]
2. Resolve Paths
├─> For each model:
│ ├─> Check src/features/{kebab-name}
│ ├─> Check src/infrastructure/{kebab-name}
│ └─> Prompt to create if missing
└─> Build ModulePathInfo[]
3. Generate DTOs
├─> For each model:
│ ├─> Generate Create DTO
│ │ ├─> Add class-validator decorators
│ │ ├─> Apply custom validation
│ │ └─> Write create-{model}.tg.dto.ts
│ └─> Generate Update DTO
│ ├─> Make all fields optional
│ ├─> Apply validation
│ └─> Write update-{model}.tg.dto.ts
4. Generate Services
├─> For each model:
│ ├─> Generate CRUD methods
│ ├─> Add pagination/search
│ ├─> Handle unique constraints
│ └─> Write {model}.tg.service.ts
5. Generate Controllers
├─> For each model:
│ ├─> Generate REST endpoints
│ ├─> Add guards based on isAdmin
│ ├─> Add validation pipes
│ └─> Write {model}.tg.controller.ts
6. Update Modules
├─> For each model:
│ ├─> Read existing module file
│ ├─> Add providers/controllers
│ └─> Write updated module
7. Update AppModule
├─> Collect all module imports
├─> Update auto-generated section
└─> Write app.module.ts
8. Update Data Provider (optional)
├─> Build endpoint mappings
├─> Update auto-generated section
└─> Write dataProvider.ts
Dashboard Generation Pipeline
1. Parse Schema
(Same as API generation)
2. Generate Components
├─> For each model:
│ ├─> Generate List
│ │ ├─> Build Datagrid columns
│ │ ├─> Add search/filter
│ │ └─> Write {Model}List.tsx
│ ├─> Generate Edit
│ │ ├─> Build form inputs
│ │ ├─> Handle relations
│ │ └─> Write {Model}Edit.tsx
│ ├─> Generate Create
│ │ (Similar to Edit)
│ ├─> Generate Show
│ │ ├─> Build read-only fields
│ │ └─> Write {Model}Show.tsx
│ ├─> Generate Studio
│ │ ├─> Build spreadsheet view
│ │ └─> Write {Model}Studio.tsx
│ └─> Generate Index
│ └─> Write index.ts
3. Generate Field Directives
├─> Extract all directives
├─> Build metadata object
└─> Write fieldDirectives.generated.ts
4. Update App.tsx
├─> Add resource imports
├─> Add Resource components
├─> Add studio routes
└─> Write App.tsx
Design Patterns
1. Strategy Pattern
Generators use strategy pattern for flexible code generation:
interface IGenerator {
generate(input: any): void | Promise<void>;
}
class ApiGenerator implements IGenerator {
async generate() {
// API-specific generation
}
}
class DashboardGenerator implements IGenerator {
async generate() {
// Dashboard-specific generation
}
}
2. Template Method Pattern
Base generators define the workflow, subclasses implement steps:
abstract class BaseGenerator {
async generate() {
await this.parse();
await this.validate();
await this.createFiles();
await this.format();
}
abstract parse(): Promise<void>;
abstract validate(): Promise<void>;
abstract createFiles(): Promise<void>;
}
3. Factory Pattern
Component generators create specific artifacts:
class ReactComponentFactory {
createList(model: PrismaModel): string {
// Generate list component
}
createEdit(model: PrismaModel): string {
// Generate edit component
}
}
4. Observer Pattern
Directive system notifies components of field metadata:
class FieldDirectiveManager {
private directives: BaseFieldDirective[] = [];
apply(field: PrismaField, sourceText: string): void {
for (const directive of this.directives) {
directive.apply(field, sourceText);
}
}
}
Data Flow
Prisma Schema → Generated Code
┌─────────────────┐
│ Prisma Schema │
│ // @tg_form() │
│ model User { │
│ id String │
│ name String │
│ } │
└────────┬────────┘
│
▼
┌─────────────────┐
│ Parsed Model │
│ { │
│ name: 'User', │
│ fields: [...] │
│ } │
└────────┬────────┘
│
┌────┴────┐
│ │
▼ ▼
┌────────┐ ┌──────────┐
│ Backend│ │ Frontend │
└────┬───┘ └────┬─────┘
│ │
▼ ▼
┌────┐ ┌──────┐
│DTOs│ │ List │
│Svc │ │ Edit │
│Ctrl│ │Create│
└────┘ └──────┘
Extension Points
1. Custom Directives
Add new field directives:
class CustomDirective extends BaseFieldDirective {
readonly name = 'tg_custom';
readonly pattern = /@tg_custom\(([^)]+)\)/;
applyMatch(field: PrismaField, match: RegExpMatchArray): void {
field.customMetadata = match[1];
}
}
// Register
FieldDirectiveManager.register(new CustomDirective());
2. Custom Generators
Create custom generators for specific needs:
class ApiDocsGenerator implements IGenerator {
async generate(models: PrismaModel[]) {
// Generate API documentation
}
}
3. Custom Updaters
Extend updaters for custom file modifications:
class CustomModuleUpdater extends NestModuleUpdater {
async update(path: string, updates: ModuleUpdates) {
// Custom pre-processing
await super.update(path, updates);
// Custom post-processing
}
}
Next Steps
- Philosophy – Design principles
- Contributing – Contribute to the project
- SDK Reference – Extend the system