スキル一覧に戻る

nestjs-typeorm-integration

AgentiveCity / SkillFactory

1🍴 0📅 2025年12月10日

Use this skill whenever the user wants to set up, configure, or refactor TypeORM within a NestJS TypeScript project, including data sources, entities, migrations, repositories, relations, and transactional patterns.

SKILL.md

---
name: "nestjs-typeorm-integration"
description: "Use this skill whenever the user wants to set up, configure, or refactor TypeORM within a NestJS TypeScript project, including data sources, entities, migrations, repositories, relations, and transactional patterns."
---

# NestJS + TypeORM Integration Skill

## Purpose

You are a specialized assistant for **integrating TypeORM with NestJS** in a clean, scalable,
and production-friendly way.

Use this skill to:

- Set up **TypeORM** in a NestJS project (data source, modules, config)
- Define or refactor **entities** and their relations
- Configure **migrations** and environment-specific DB settings
- Wire **repositories** into services using Nest DI
- Implement **transactions** and **query patterns** safely
- Optimize DB usage (indexes, query patterns, relations loading) at a structural level

Do **not** use this skill for:

- General NestJS module/service/controller scaffolding → use `nestjs-project-scaffold` / `nestjs-modules-services-controllers`
- Authentication logic → use `nestjs-authentication`
- Supabase-specific flows → use Supabase skills (unless Supabase Postgres is accessed via TypeORM as a plain DB)

If `CLAUDE.md` exists, follow its rules on database choice, naming conventions, and directory layout.

---

## When To Apply This Skill

Trigger this skill when the user asks for things like:

- “Set up TypeORM in this NestJS API.”
- “Create entities and migrations for these tables in NestJS + TypeORM.”
- “Wire repositories into my Nest services.”
- “Fix or refactor our NestJS TypeORM config.”
- “Add relations between these entities and update the service logic.”
- “Handle transactions for this multi-step operation.”

Avoid using this skill when:

- Only high-level REST API contracts are changing without DB impact.
- Only pure in-memory logic is being implemented.

---

## Assumptions & Defaults

Unless the project states otherwise, assume:

- Database: Postgres (can be adapted to MySQL, SQLite, etc.)
- TypeORM version: current stable for NestJS
- Connection is configured via Nest’s `TypeOrmModule`
- Config is environment-driven via `@nestjs/config` and `.env` files
- Entities live in `src/modules/<feature>/entities` or `src/entities` depending on project style
- Migrations live in `src/migrations` or `migrations` directory

---

## High-Level Architecture

Recommended structure (adapt as needed):

```text
project-root/
  src/
    config/
      database.config.ts
    modules/
      user/
        user.module.ts
        user.service.ts
        user.controller.ts
        entities/
          user.entity.ts
      post/
        post.module.ts
        post.service.ts
        post.controller.ts
        entities/
          post.entity.ts
    infrastructure/
      database/
        ormconfig.ts or data-source.ts (optional central place)
  migrations/
    1710000000000-CreateUserTable.ts
    1710000001000-CreatePostTable.ts
```

This skill should align with the existing structure rather than forcing a totally new one, unless the project is greenfield.

---

## Step-by-Step Workflow

When this skill is active, follow these steps:

### 1. Set Up TypeORM Module Configuration

If TypeORM is not configured yet:

- Install TypeORM + DB driver for the chosen database.
- Configure `TypeOrmModule` in `AppModule` or a dedicated `DatabaseModule`.

Example using `@nestjs/config`:

```ts
// src/config/database.config.ts
import { registerAs } from "@nestjs/config";

export default registerAs("database", () => ({
  type: "postgres",
  host: process.env.DB_HOST ?? "localhost",
  port: parseInt(process.env.DB_PORT ?? "5432", 10),
  username: process.env.DB_USERNAME ?? "postgres",
  password: process.env.DB_PASSWORD ?? "postgres",
  database: process.env.DB_NAME ?? "app_db",
}));
```

```ts
// src/app.module.ts
import { Module } from "@nestjs/common";
import { ConfigModule, ConfigService } from "@nestjs/config";
import databaseConfig from "./config/database.config";
import { TypeOrmModule } from "@nestjs/typeorm";

@Module({
  imports: [
    ConfigModule.forRoot({
      isGlobal: true,
      load: [databaseConfig],
    }),
    TypeOrmModule.forRootAsync({
      inject: [ConfigService],
      useFactory: (config: ConfigService) => {
        const db = config.get("database");
        return {
          ...db,
          autoLoadEntities: true,
          synchronize: false, // prefer migrations in production
        };
      },
    }),
    // feature modules...
  ],
})
export class AppModule {}
```

Key rules:

- `synchronize: false` in all non-dev environments (this skill encourages migrations).
- `autoLoadEntities: true` is acceptable for many apps; for stricter control, explicitly list entities.

### 2. Environment Variables

Ensure `.env` (and `.env.example`) contain:

```env
DB_HOST=localhost
DB_PORT=5432
DB_USERNAME=postgres
DB_PASSWORD=postgres
DB_NAME=app_db
```

This skill should help keep secrets out of code and only in env/config.

### 3. Entities Design

For each feature, create entity classes:

```ts
// src/modules/user/entities/user.entity.ts
import {
  Column,
  CreateDateColumn,
  Entity,
  PrimaryGeneratedColumn,
  UpdateDateColumn,
} from "typeorm";

@Entity({ name: "users" })
export class User {
  @PrimaryGeneratedColumn("uuid")
  id!: string;

  @Column({ unique: true })
  email!: string;

  @Column()
  passwordHash!: string;

  @Column({ default: true })
  isActive!: boolean;

  @CreateDateColumn()
  createdAt!: Date;

  @UpdateDateColumn()
  updatedAt!: Date;
}
```

Relations example:

```ts
// src/modules/post/entities/post.entity.ts
import {
  Column,
  CreateDateColumn,
  Entity,
  ManyToOne,
  PrimaryGeneratedColumn,
} from "typeorm";
import { User } from "../../user/entities/user.entity";

@Entity({ name: "posts" })
export class Post {
  @PrimaryGeneratedColumn("uuid")
  id!: string;

  @Column()
  title!: string;

  @Column({ type: "text" })
  content!: string;

  @ManyToOne(() => User, (user) => user.posts, { onDelete: "CASCADE" })
  author!: User;

  @CreateDateColumn()
  createdAt!: Date;
}
```

This skill should:

- Encourage using `uuid` or bigint for IDs consistently (per project preference).
- Use clear relation options (`onDelete`, `eager`, `lazy`) thoughtfully.
- Avoid putting heavy business logic directly into entities.

### 4. Module & Repository Wiring

Use `TypeOrmModule.forFeature` to inject repositories into feature modules:

```ts
// src/modules/user/user.module.ts
import { Module } from "@nestjs/common";
import { TypeOrmModule } from "@nestjs/typeorm";
import { User } from "./entities/user.entity";
import { UserService } from "./user.service";
import { UserController } from "./user.controller";

@Module({
  imports: [TypeOrmModule.forFeature([User])],
  controllers: [UserController],
  providers: [UserService],
  exports: [UserService],
})
export class UserModule {}
```

In `UserService`, inject the repository:

```ts
// src/modules/user/user.service.ts
import { Injectable } from "@nestjs/common";
import { InjectRepository } from "@nestjs/typeorm";
import { Repository } from "typeorm";
import { User } from "./entities/user.entity";
import { CreateUserDto } from "./dto/create-user.dto";

@Injectable()
export class UserService {
  constructor(
    @InjectRepository(User)
    private readonly usersRepo: Repository<User>,
  ) {}

  create(dto: CreateUserDto) {
    const entity = this.usersRepo.create({
      email: dto.email,
      passwordHash: dto.passwordHash,
    });
    return this.usersRepo.save(entity);
  }

  findAll() {
    return this.usersRepo.find();
  }

  findOne(id: string) {
    return this.usersRepo.findOne({ where: { id } });
  }

  // etc...
}
```

This skill should enforce:

- Repositories are injected via DI, not instantiated manually.
- Services depend on repositories, not on the data source directly (except in advanced scenarios).

### 5. Migrations

Encourage using migrations instead of `synchronize` for schema changes.

- Create a `data-source.ts` file if needed for CLI migrations:

```ts
// data-source.ts (or src/infrastructure/database/data-source.ts)
import "reflect-metadata";
import { DataSource } from "typeorm";
import databaseConfig from "./src/config/database.config";
import { config as loadEnv } from "dotenv";

loadEnv();

const db = databaseConfig();

export const AppDataSource = new DataSource({
  type: "postgres",
  host: db.database.host,
  port: db.database.port,
  username: db.database.username,
  password: db.database.password,
  database: db.database.database,
  entities: ["src/**/*.entity.{ts,js}"],
  migrations: ["migrations/*.{ts,js}"],
});
```

- Add package.json scripts for migrations (exact form depends on project):

```jsonc
{
  "scripts": {
    "typeorm:run": "typeorm-ts-node-commonjs migration:run -d data-source.ts",
    "typeorm:revert": "typeorm-ts-node-commonjs migration:revert -d data-source.ts",
    "typeorm:generate": "typeorm-ts-node-commonjs migration:generate -d data-source.ts migrations/AutoMigration"
  }
}
```

This skill should:

- Prefer explicit migration generation (`migration:generate`) over schema sync.
- Keep migration files small, ordered, and committed to version control.

### 6. Transactions & Complex Operations

For operations that require multiple DB writes, this skill should:

- Use `QueryRunner` or `manager.transaction` where needed.

Example:

```ts
import { DataSource } from "typeorm";

@Injectable()
export class OrderService {
  constructor(private readonly dataSource: DataSource) {}

  async createOrderAndItems(dto: CreateOrderDto) {
    return this.dataSource.transaction(async (manager) => {
      const order = manager.create(Order, { /* ... */ });
      await manager.save(order);

      const items = dto.items.map((itemDto) =>
        manager.create(OrderItem, {
          order,
          // ...
        }),
      );
      await manager.save(items);

      return order;
    });
  }
}
```

Guidelines:

- Use transactions only where needed; avoid wrapping everything by default.
- Handle error propagation correctly; if the transaction throws, it rolls back.

### 7. Performance & Query Patterns

This skill should guide towards:

- Using `select` and projections instead of always loading entire entities.
- Avoiding N+1 queries with relation loading patterns when necessary.
- Adding indexes in migrations for frequently queried columns.
- Using pagination strategies (offset/limit or cursor-based) for large lists.

### 8. Refactoring Existing TypeORM Usage

When refactoring:

- Identify anti-patterns:
  - Manual connection creation (bypassing Nest DI)
  - Direct use of global `getRepository` instead of injected repositories
  - `synchronize: true` in production
- Replace with:
  - `TypeOrmModule` configuration
  - Injected `Repository<T>` or `DataSource`
  - Migrations for schema changes

This skill should try to minimize breaking changes while improving structure.

---

## Interaction with Other Skills

- `nestjs-project-scaffold`:
  - Provides the base Nest structure; this skill plugs DB configuration into it.
- `nestjs-modules-services-controllers`:
  - Uses modules/services; this skill adds entities + repositories behind those services.
- `nestjs-authentication`:
  - Depends on user entities and user repository; this skill provides that layer.
- TypeORM-specific skills (`typeorm-schema-design`, `typeorm-migrations-workflow`):
  - Can be used in addition for deeper DB design and migration strategies.

---

## Example Prompts That Should Use This Skill

- “Connect this NestJS app to Postgres via TypeORM and create User & Post entities.”
- “Refactor this hand-rolled DB code into proper TypeORM modules and services.”
- “Add migrations for these schema changes and wire them into our NestJS project.”
- “Implement a transactional operation that creates an order and its items.”
- “Fix this TypeORM config; it works locally but fails in production.”

For such prompts, rely on this skill to design and implement **NestJS + TypeORM integration** that
is robust, maintainable, and ready for production, while delegating non-DB concerns to other skills.