| 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. |
Creating Entities
Scaffolds complete entity definitions following the project's EntitySchema pattern — interface, schema, migration, and validation in one workflow.
Creation Process
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:
- Interface name: PascalCase (
ResourceName)
- Variable name:
{PascalCase}Entity (ResourceNameEntity)
- Table name: snake_case (
resource_name)
- Column names: camelCase in TypeScript, quoted in SQL
- Primary keys:
type: "int", generated: "increment"
- Timestamps:
type: "timestamptz" with createDate: true / updateDate: true
- Nullable fields: explicitly
nullable: true
- Index names:
UQ_ for unique, IDX_ for non-unique
- Soft delete: nullable
deletedAt column + partial unique index with where: '"deletedAt" IS NULL'
- Enum columns: define enums in
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:
- Raw SQL only — no TypeORM schema builder
- Quote column names with double quotes for PostgreSQL camelCase preservation
down() drops in reverse order (indices before tables)
- Class name:
{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.ts is 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 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
- Run Docker to test migrations:
docker compose up
- Check types:
npx tsc --noEmit
- Check linting:
npm run lint
- Run tests:
npx vitest run
Connected Skills
creating-api-routes — create API endpoints for the new entity
writing-integration-tests — write tests for entity operations