en un clic
creating-entities
// Scaffold TypeORM EntitySchema definitions with TypeScript interfaces, database migrations, and Zod validation schemas. Use when creating new database entities, adding tables, or modifying existing entity structures.
// Scaffold TypeORM EntitySchema definitions with TypeScript interfaces, database migrations, and Zod validation schemas. Use when creating new database entities, adding tables, or modifying existing entity structures.
| name | creating-entities |
| description | Scaffold TypeORM EntitySchema definitions with TypeScript interfaces, database migrations, and Zod validation schemas. Use when creating new database entities, adding tables, or modifying existing entity structures. |
Scaffolds complete entity definitions following the project's EntitySchema pattern — interface, schema, migration, and validation in one workflow.
Use the checklist below and track your progress:
Progress:
- [ ] Step 1: Define the entity interface and schema
- [ ] Step 2: Create the database migration
- [ ] Step 3: Register the entity
- [ ] Step 4: Create the validation schema
- [ ] Step 5: Verify everything works
Step 1: Define the entity interface and schema
Create src/entities/{entity-name}.entity.ts:
import { EntitySchema } from "typeorm";
export interface ResourceName {
id: number;
name: string;
description: string | null;
createdAt: Date;
updatedAt: Date;
}
export const ResourceNameEntity = new EntitySchema<ResourceName>({
name: "ResourceName",
tableName: "resource_name",
columns: {
id: { type: "int", primary: true, generated: "increment" },
name: { type: "varchar", length: 255 },
description: { type: "varchar", length: 1000, nullable: true },
createdAt: { type: "timestamptz", createDate: true },
updatedAt: { type: "timestamptz", updateDate: true },
},
indices: [
{ name: "UQ_resource_name_name", columns: ["name"], unique: true },
],
});
Conventions:
ResourceName){PascalCase}Entity (ResourceNameEntity)resource_name)type: "int", generated: "increment"type: "timestamptz" with createDate: true / updateDate: truenullable: trueUQ_ for unique, IDX_ for non-uniquedeletedAt column + partial unique index with where: '"deletedAt" IS NULL'src/entities/enums.ts and import from @/entities/enums (e.g., import { SeatStatus } from "@/entities/enums")Step 2: Create the database migration
Create migrations/{timestamp}-Create{EntityName}.ts. Generate a timestamp with Date.now().
import { MigrationInterface, QueryRunner } from "typeorm";
export class CreateResourceName{timestamp} implements MigrationInterface {
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`
CREATE TABLE "resource_name" (
"id" SERIAL PRIMARY KEY,
"name" VARCHAR(255) NOT NULL,
"description" VARCHAR(1000),
"createdAt" TIMESTAMPTZ NOT NULL DEFAULT now(),
"updatedAt" TIMESTAMPTZ NOT NULL DEFAULT now()
)
`);
await queryRunner.query(`
CREATE UNIQUE INDEX "UQ_resource_name_name" ON "resource_name" ("name")
`);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`DROP INDEX "UQ_resource_name_name"`);
await queryRunner.query(`DROP TABLE "resource_name"`);
}
}
Rules:
down() drops in reverse order (indices before tables){PascalCaseDescription}{Timestamp}Step 3: Register the entity
Add the entity import to src/lib/data-source.shared.ts in the entities array.
Note:
data-source.shared.tsis an exception to the@/import rule — it uses relative imports because it is shared with the TypeORM CLI which does not understand path aliases.
import { ResourceNameEntity } from "../entities/resource-name.entity";
// Add to the entities array
Add the migration import to src/lib/data-source.cli.ts in the migrations array.
Step 4: Create the validation schema
Create src/lib/validations/{entity-name}.ts:
import { z } from "zod";
import { nameFieldSchema } from "@/lib/validations/shared";
export const createResourceNameSchema = z.object({
name: nameFieldSchema,
description: z.string().max(1000).nullable().optional(),
});
export type CreateResourceNameInput = z.infer<typeof createResourceNameSchema>;
Check src/lib/validations/shared.ts first for reusable field schemas.
Step 5: Verify everything works
docker compose upnpx tsc --noEmitnpm run lintnpx vitest runcreating-api-routes — create API endpoints for the new entitywriting-integration-tests — write tests for entity operationsScaffold new Next.js API route handlers with auth guards, Zod validation, error handling, and integration tests. Use when creating new API endpoints, adding CRUD operations, or building route handlers.
Scaffold background jobs with node-cron scheduling, pessimistic locking via acquireJobLock, and instrumentation.ts registration. Use when creating scheduled jobs, adding cron tasks, or implementing background processing.
Write Playwright E2E tests with direct DB seeding, API-based login, accessible selectors, and dual-config awareness. Use when creating end-to-end tests, testing user flows, or verifying UI behavior.
Write Vitest integration tests against a real PostgreSQL test database with proper mock setup, seed functions, and assertion patterns. Use when creating tests for API routes, lib modules, or database operations.