Summary
Replace NestJS generator's in-memory array storage with real database persistence using TypeORM. This is critical for production readiness and enables data integrity, transactions, and multi-instance deployments.
Motivation
Current NestJS generation produces a demo-quality project with hardcoded in-memory storage:
// Current - loses data on restart
private readonly items: Produto[] = [];
This severely limits usefulness:
- No persistence between restarts
- Single-process only (no scaling)
- No transactions or ACID guarantees
- No real database schema
- Cannot demonstrate production patterns
With TypeORM integration, generated projects become production-ready:
- SQL database backend (PostgreSQL, MySQL, SQLite)
- Entity relationship mapping
- Migrations and schema management
- Transaction support
- Query optimization
Scope
In Scope
Out of Scope
- Relationships/foreign keys (v0.3 Feature 2.2)
- Cascade operations
- Query optimization/indexes
- Database connection pooling
- Migrations generation from schema changes
Technical Design
Generated Entity Example
Input:
namespace financeiro
contract Produto {
id: uuid
nome: string
descricao?: string
valor: decimal
ativo: boolean
criadoEm: datetime
}
Output (src/produto/entities/produto.entity.ts):
import { Entity, PrimaryGeneratedColumn, Column } from 'typeorm';
@Entity('financeiro_produto')
export class ProdutoEntity {
@PrimaryGeneratedColumn('uuid')
id: string;
@Column()
nome: string;
@Column({ nullable: true })
descricao?: string;
@Column('decimal', { precision: 10, scale: 2 })
valor: number;
@Column()
ativo: boolean;
@Column('timestamp')
criadoEm: Date;
}
Repository Pattern
Generated (src/produto/repositories/produto.repository.ts):
import { Injectable } from '@nestjs/common';
import { DataSource, Repository } from 'typeorm';
import { ProdutoEntity } from '../entities/produto.entity';
@Injectable()
export class ProdutoRepository extends Repository<ProdutoEntity> {
constructor(private dataSource: DataSource) {
super(ProdutoEntity, dataSource.createEntityManager());
}
async findAllActive(): Promise<ProdutoEntity[]> {
return this.find({ where: { ativo: true } });
}
}
Service Integration
Update service to use repository:
@Injectable()
export class ProdutoService {
constructor(private readonly repository: ProdutoRepository) {}
async findAll(): Promise<ProdutoEntity[]> {
return this.repository.find();
}
async findOne(id: string): Promise<ProdutoEntity> {
const produto = await this.repository.findOne({ where: { id } });
if (!produto) {
throw new NotFoundException(`Produto ${id} not found`);
}
return produto;
}
async create(createDto: CreateProdutoDto): Promise<ProdutoEntity> {
return this.repository.save(createDto);
}
async update(id: string, updateDto: UpdateProdutoDto): Promise<ProdutoEntity> {
await this.findOne(id);
return this.repository.save({ id, ...updateDto });
}
async remove(id: string): Promise<void> {
const result = await this.repository.delete(id);
if (result.affected === 0) {
throw new NotFoundException(`Produto ${id} not found`);
}
}
}
Database Configuration
Generate src/database.config.ts:
import { DataSourceOptions } from 'typeorm';
export const dataSourceOptions: DataSourceOptions = process.env.DATABASE_URL
? {
type: 'postgres',
url: process.env.DATABASE_URL,
entities: ['dist/**/*.entity.js'],
migrations: ['dist/migrations/*.js'],
synchronize: false,
}
: {
type: 'sqlite',
database: 'dev.db',
entities: ['dist/**/*.entity.js'],
migrations: ['dist/migrations/*.js'],
synchronize: true,
};
Generate src/main.ts:
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { DataSource } from 'typeorm';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
const dataSource = app.get(DataSource);
if (!dataSource.isInitialized) {
await dataSource.initialize();
await dataSource.runMigrations();
}
await app.listen(3000);
}
bootstrap();
Type Mapping
Forge Type → TypeORM Column Type → SQL Type
string → varchar → VARCHAR
int → int → INTEGER
float → float → FLOAT
decimal → decimal(10,2) → NUMERIC
boolean → boolean → BOOLEAN
uuid → uuid → UUID
datetime → timestamp → TIMESTAMP
date → date → DATE
Dependencies to Add
Update generated package.json:
{
"dependencies": {
"@nestjs/common": "^10.0.0",
"@nestjs/core": "^10.0.0",
"@nestjs/typeorm": "^9.0.0",
"typeorm": "^0.3.0",
"sqlite3": "^5.1.0"
},
"devDependencies": {
"typeorm": "^0.3.0"
}
}
Acceptance Criteria
Testing Strategy
Unit Tests
// tests/nest-typeorm.test.mjs
test('generated entity has correct decorators', () => {
// Verify @Entity, @Column decorators present
})
test('repository extends TypeORM Repository', () => {
// Verify ProdutoRepository extends Repository<ProdutoEntity>
})
test('service uses repository in findAll', () => {
// Verify service calls this.repository.find()
})
Integration Tests
// tests/nest-database-integration.test.mjs
test('create produto persists to database', async () => {
const app = await createTestApp();
const response = await app.post('/produto').send({
nome: 'Test Product',
valor: 99.99
});
expect(response.status).toBe(201);
const id = response.body.id;
// Verify in database
const db = app.get(DataSource);
const saved = await db.getRepository(ProdutoEntity).findOne({
where: { id }
});
expect(saved.nome).toBe('Test Product');
})
test('update produto persists changes', async () => {
// Create, update, verify in DB
})
test('delete removes from database', async () => {
// Create, delete, verify gone from DB
})
test('restart preserves data in SQLite', async () => {
// Create product
// Stop app
// Restart app
// Verify product still exists
})
Database Tests
- Start SQLite in-memory database
- Run migrations
- CRUD operations
- Transaction handling
- Error cases (404, constraint violations)
Files to Create/Modify
New Files:
packages/generators/src/nest/templates/entity.template.ts
packages/generators/src/nest/templates/repository.template.ts
packages/generators/src/nest/typeorm/column-mapper.ts
tests/nest-database-integration.test.mjs (200 lines)
Modified Files:
packages/generators/src/nest/generator.ts (+50 lines)
packages/generators/src/nest/templates/service.template.ts (full rewrite)
packages/generators/src/nest/templates/module.template.ts (+imports)
Non-Goals
- Migrations from Forge contract changes
- Query builder DSL
- Query optimization
- Relationship cascade operations
- Soft deletes
Future Extensions
- Automatic migrations from schema changes
- Query logging and debugging
- Connection pooling configuration
- Read replicas support
- Change data capture (CDC)
- Audit trail (created_by, updated_by)
Dependencies
Blocks:
- Feature 1.2.2: Complete CRUD Endpoints (depends on DB working)
- Feature 1.2.3: Swagger Documentation (depends on working endpoints)
- Feature 2.2.5: Relationship support (depends on base TypeORM setup)
Depends On:
- Phase 0: Basic NestJS generation (✅ complete)
Implementation Notes
Column Type Precision
For decimal fields, use precision and scale:
@Column('decimal', { precision: 10, scale: 2 })
valor: number;
Date Handling
Map datetime → TIMESTAMP in SQL:
@Column('timestamp')
criadoEm: Date;
Entity Naming Convention
Table name: lowercase namespace + contract:
contract User → table: user
namespace auth, contract User → table: auth_user
Index Strategy
Always index primary key (auto). Future: add indexes on frequently queried fields.
Summary
Replace NestJS generator's in-memory array storage with real database persistence using TypeORM. This is critical for production readiness and enables data integrity, transactions, and multi-instance deployments.
Motivation
Current NestJS generation produces a demo-quality project with hardcoded in-memory storage:
This severely limits usefulness:
With TypeORM integration, generated projects become production-ready:
Scope
In Scope
Out of Scope
Technical Design
Generated Entity Example
Input:
Output (
src/produto/entities/produto.entity.ts):Repository Pattern
Generated (
src/produto/repositories/produto.repository.ts):Service Integration
Update service to use repository:
Database Configuration
Generate
src/database.config.ts:Generate
src/main.ts:Type Mapping
Dependencies to Add
Update generated
package.json:{ "dependencies": { "@nestjs/common": "^10.0.0", "@nestjs/core": "^10.0.0", "@nestjs/typeorm": "^9.0.0", "typeorm": "^0.3.0", "sqlite3": "^5.1.0" }, "devDependencies": { "typeorm": "^0.3.0" } }Acceptance Criteria
Testing Strategy
Unit Tests
Integration Tests
Database Tests
Files to Create/Modify
New Files:
packages/generators/src/nest/templates/entity.template.tspackages/generators/src/nest/templates/repository.template.tspackages/generators/src/nest/typeorm/column-mapper.tstests/nest-database-integration.test.mjs(200 lines)Modified Files:
packages/generators/src/nest/generator.ts(+50 lines)packages/generators/src/nest/templates/service.template.ts(full rewrite)packages/generators/src/nest/templates/module.template.ts(+imports)Non-Goals
Future Extensions
Dependencies
Blocks:
Depends On:
Implementation Notes
Column Type Precision
For
decimalfields, useprecisionandscale:Date Handling
Map datetime → TIMESTAMP in SQL:
Entity Naming Convention
Table name: lowercase namespace + contract:
Index Strategy
Always index primary key (auto). Future: add indexes on frequently queried fields.