Guidelines for developing with TypeORM, a full-featured ORM for TypeScript and JavaScript supporting multiple databases

MPL-2.0Auto-check passedDatabases

Install Typeorm

skills CLI
$ npx skills add rolling-scopes/rsschool-app --skill typeorm -a claude-code

Project install by default; add -g for ~/.claude/skills/.

GitHub CLI
$ gh skill install rolling-scopes/rsschool-app typeorm --agent claude-code

Project scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).

Manual copy
$ git clone --depth 1 https://github.com/rolling-scopes/rsschool-app.git skills-src && mkdir -p .claude/skills && cp -r skills-src/.agents/skills/typeorm .claude/skills/typeorm && rm -rf skills-src

Use ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.

Claude Code skills documentation · loads skills from .claude/skills/

Facts

Skill name
typeorm
GitHub stars
10k
Used in
1 other repo
Token cost
~3.5k tokens
SKILL.md length
148 words
Files
1
Skills in repo
3
Repo updated
First seen
Licence
MPL-2.0

At a glance

Guidelines for developing with TypeORM, a full-featured ORM for TypeScript and JavaScript supporting multiple databases

  • Tasks that involve ORMs and data access
  • SKILL.md covers Core Principles, TypeScript Configuration, Entity Definition and Relationships, plus 6 more sections
  • Calls npx; needs DB_PASSWORD

What it does

Typeorm is an agent skill from rolling-scopes/rsschool-app. Guidelines for developing with TypeORM, a full-featured ORM for TypeScript and JavaScript supporting multiple databases

Its SKILL.md is about 3.5k tokens, which your agent loads only when the skill is triggered. It is a single SKILL.md file with no bundled scripts.

It sits in Databases, covering ORMs and data access. It works with TypeORM, TypeScript and JavaScript. The repository describes itself as: An application for the RS School education process. The licence is MPL-2.0.

When your agent uses it

  • Tasks that involve ORMs and data access

Example prompts

  • “Use the typeorm skill to guideline for developing with TypeORM, a full-featured ORM for TypeScript and JavaScript supporting multiple databases”
  • “/typeorm”

Requirements

  • Node.js

What it can do on your machine

Read from SKILL.md and the folder at commit f741ad2. It shows what the files ask for, not the result of running them.

  • Tool permissions

    Pre-approves nothing: there is no allowed-tools line, so your agent's usual permission prompts apply.

    From allowed-tools in the SKILL.md frontmatter.

  • Runs code

    Shell commands in SKILL.md call:

    • npx

    From the folder's file list and the shell code blocks in SKILL.md.

  • Network

    No URLs in SKILL.md. Its commands use npx, which can reach the network depending on how they are called.

    From URLs in SKILL.md, links to its own repository left out.

  • Credentials

    Names these keys or tokens, usually read from environment variables:

    • DB_PASSWORD

    From names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.

Context cost

Typeorm loads about 3.5k tokens when it runs. Until then it costs about 32 tokens; SKILL.md has 148 words of instructions outside code blocks.

Always · name and description, kept in context so the agent knows when to use it
~32
When it runs · the whole SKILL.md, loaded when a task matches
~3.5k

Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.

Safety

Auto-check passed

The automated check found no risky patterns in SKILL.md.

Automated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); files beside SKILL.md are not scanned.

SKILL.md

The full file from rolling-scopes/rsschool-app at commit f741ad2, republished under its MPL-2.0 licence (© rolling-scopes). 148 words, ~3,474 tokens.

Download SKILL.mdSave it as .claude/skills/typeorm/SKILL.md (or your agent's skills folder).
name
typeorm
description
Guidelines for developing with TypeORM, a full-featured ORM for TypeScript and JavaScript supporting multiple databases

TypeORM Development Guidelines

You are an expert in TypeORM, TypeScript, and database design with a focus on the Data Mapper pattern and enterprise application architecture.

Core Principles

  • TypeORM supports both Active Record and Data Mapper patterns
  • Uses TypeScript decorators for entity and column definitions
  • Supports MySQL, PostgreSQL, MariaDB, SQLite, MS SQL Server, Oracle, and more
  • Works in Node.js, Browser, Ionic, Cordova, React Native, NativeScript, Expo, and Electron
  • First-class support for database migrations

TypeScript Configuration

Required settings in tsconfig.json:

json
{
  "compilerOptions": {
    "experimentalDecorators": true,
    "emitDecoratorMetadata": true,
    "strict": true,
    "target": "ES2020",
    "module": "commonjs",
    "moduleResolution": "node"
  }
}

Entity Definition

Basic Entity
typescript
import { Entity, PrimaryGeneratedColumn, Column, CreateDateColumn, UpdateDateColumn } from 'typeorm';

@Entity('users')
export class User {
  @PrimaryGeneratedColumn()
  id: number;

  @Column({ type: 'varchar', length: 255, unique: true })
  email: string;

  @Column({ type: 'varchar', length: 255, nullable: true })
  name: string | null;

  @Column({ type: 'boolean', default: true })
  isActive: boolean;

  @CreateDateColumn()
  createdAt: Date;

  @UpdateDateColumn()
  updatedAt: Date;
}
Primary Key Options
typescript
// Auto-increment
@PrimaryGeneratedColumn()
id: number;

// UUID
@PrimaryGeneratedColumn("uuid")
id: string;

// Custom primary key
@PrimaryColumn()
id: string;

// Composite primary key
@Entity()
export class OrderItem {
  @PrimaryColumn()
  orderId: number;

  @PrimaryColumn()
  productId: number;
}
Column Decorators
typescript
@Entity()
export class Product {
  @PrimaryGeneratedColumn()
  id: number;

  // String columns
  @Column({ type: 'varchar', length: 255 })
  name: string;

  @Column({ type: 'text', nullable: true })
  description: string | null;

  // Numeric columns
  @Column({ type: 'decimal', precision: 10, scale: 2 })
  price: number;

  @Column({ type: 'int', default: 0 })
  stock: number;

  // Boolean
  @Column({ type: 'boolean', default: true })
  isAvailable: boolean;

  // JSON
  @Column({ type: 'jsonb', nullable: true })
  metadata: Record<string, any> | null;

  // Enum
  @Column({
    type: 'enum',
    enum: ['active', 'inactive', 'pending'],
    default: 'pending',
  })
  status: 'active' | 'inactive' | 'pending';

  // Timestamps
  @CreateDateColumn()
  createdAt: Date;

  @UpdateDateColumn()
  updatedAt: Date;

  @DeleteDateColumn()
  deletedAt: Date | null; // For soft deletes

  // Version column for optimistic locking
  @VersionColumn()
  version: number;
}

Relationships

One-to-One
typescript
@Entity()
export class User {
  @PrimaryGeneratedColumn()
  id: number;

  @OneToOne(() => Profile, profile => profile.user, { cascade: true })
  @JoinColumn()
  profile: Profile;
}

@Entity()
export class Profile {
  @PrimaryGeneratedColumn()
  id: number;

  @Column()
  bio: string;

  @OneToOne(() => User, user => user.profile)
  user: User;
}
One-to-Many / Many-to-One
typescript
@Entity()
export class User {
  @PrimaryGeneratedColumn()
  id: number;

  @Column()
  name: string;

  @OneToMany(() => Post, post => post.author)
  posts: Post[];
}

@Entity()
export class Post {
  @PrimaryGeneratedColumn()
  id: number;

  @Column()
  title: string;

  @ManyToOne(() => User, user => user.posts, { onDelete: 'CASCADE' })
  @JoinColumn({ name: 'author_id' })
  author: User;

  @Column()
  authorId: number; // Explicit foreign key column
}
Many-to-Many
typescript
@Entity()
export class Post {
  @PrimaryGeneratedColumn()
  id: number;

  @Column()
  title: string;

  @ManyToMany(() => Tag, tag => tag.posts)
  @JoinTable({
    name: 'post_tags',
    joinColumn: { name: 'post_id' },
    inverseJoinColumn: { name: 'tag_id' },
  })
  tags: Tag[];
}

@Entity()
export class Tag {
  @PrimaryGeneratedColumn()
  id: number;

  @Column({ unique: true })
  name: string;

  @ManyToMany(() => Post, post => post.tags)
  posts: Post[];
}

Repository Pattern

Basic Repository Usage
typescript
import { AppDataSource } from './data-source';
import { User } from './entities/User';

const userRepository = AppDataSource.getRepository(User);

// Find all
const users = await userRepository.find();

// Find with conditions
const activeUsers = await userRepository.find({
  where: { isActive: true },
});

// Find one
const user = await userRepository.findOne({
  where: { id: 1 },
});

// Find or fail
const user = await userRepository.findOneOrFail({
  where: { id: 1 },
});

// Save
const newUser = userRepository.create({
  email: 'user@example.com',
  name: 'John Doe',
});
await userRepository.save(newUser);

// Update
await userRepository.update({ id: 1 }, { name: 'Jane Doe' });

// Delete
await userRepository.delete({ id: 1 });

// Soft delete (requires @DeleteDateColumn)
await userRepository.softDelete({ id: 1 });
Custom Repository
typescript
import { Repository, DataSource } from 'typeorm';
import { User } from './entities/User';

export class UserRepository extends Repository<User> {
  constructor(private dataSource: DataSource) {
    super(User, dataSource.createEntityManager());
  }

  async findByEmail(email: string): Promise<User | null> {
    return this.findOne({ where: { email } });
  }

  async findActiveUsers(): Promise<User[]> {
    return this.find({
      where: { isActive: true },
      order: { createdAt: 'DESC' },
    });
  }

  async findWithPosts(userId: number): Promise<User | null> {
    return this.findOne({
      where: { id: userId },
      relations: ['posts'],
    });
  }
}
Query Builder
typescript
const users = await userRepository
  .createQueryBuilder('user')
  .leftJoinAndSelect('user.posts', 'post')
  .where('user.isActive = :isActive', { isActive: true })
  .andWhere('post.publishedAt IS NOT NULL')
  .orderBy('user.createdAt', 'DESC')
  .skip(0)
  .take(10)
  .getMany();

// With raw results
const result = await userRepository
  .createQueryBuilder('user')
  .select('COUNT(*)', 'count')
  .where('user.isActive = :isActive', { isActive: true })
  .getRawOne();

// Insert with query builder
await userRepository
  .createQueryBuilder()
  .insert()
  .into(User)
  .values([
    { email: 'user1@example.com', name: 'User 1' },
    { email: 'user2@example.com', name: 'User 2' },
  ])
  .execute();

Data Source Configuration

typescript
// data-source.ts
import { DataSource } from 'typeorm';
import { User } from './entities/User';
import { Post } from './entities/Post';

export const AppDataSource = new DataSource({
  type: 'postgres',
  host: process.env.DB_HOST || 'localhost',
  port: parseInt(process.env.DB_PORT || '5432'),
  username: process.env.DB_USERNAME,
  password: process.env.DB_PASSWORD,
  database: process.env.DB_NAME,

  // Entity configuration
  entities: [User, Post],
  // Or use glob pattern: entities: ["src/entities/**/*.ts"]

  // Migrations
  migrations: ['src/migrations/**/*.ts'],

  // Synchronize - NEVER use in production
  synchronize: false,

  // Logging
  logging: process.env.NODE_ENV === 'development',

  // Connection pool
  poolSize: 10,

  // SSL (for production)
  ssl: process.env.NODE_ENV === 'production' ? { rejectUnauthorized: false } : false,
});

// Initialize connection
AppDataSource.initialize()
  .then(() => console.log('Data Source initialized'))
  .catch(error => console.error('Error initializing Data Source:', error));

Migrations

Creating Migrations
bash
# Generate migration from entity changes
npx typeorm migration:generate src/migrations/CreateUsers -d src/data-source.ts

# Create empty migration
npx typeorm migration:create src/migrations/SeedUsers

# Run migrations
npx typeorm migration:run -d src/data-source.ts

# Revert last migration
npx typeorm migration:revert -d src/data-source.ts
Migration File Structure
typescript
import { MigrationInterface, QueryRunner, Table, TableIndex } from 'typeorm';

export class CreateUsers1234567890 implements MigrationInterface {
  public async up(queryRunner: QueryRunner): Promise<void> {
    await queryRunner.createTable(
      new Table({
        name: 'users',
        columns: [
          {
            name: 'id',
            type: 'int',
            isPrimary: true,
            isGenerated: true,
            generationStrategy: 'increment',
          },
          {
            name: 'email',
            type: 'varchar',
            length: '255',
            isUnique: true,
          },
          {
            name: 'name',
            type: 'varchar',
            length: '255',
            isNullable: true,
          },
          {
            name: 'is_active',
            type: 'boolean',
            default: true,
          },
          {
            name: 'created_at',
            type: 'timestamp',
            default: 'CURRENT_TIMESTAMP',
          },
          {
            name: 'updated_at',
            type: 'timestamp',
            default: 'CURRENT_TIMESTAMP',
            onUpdate: 'CURRENT_TIMESTAMP',
          },
        ],
      }),
      true,
    );

    await queryRunner.createIndex(
      'users',
      new TableIndex({
        name: 'IDX_USERS_EMAIL',
        columnNames: ['email'],
      }),
    );
  }

  public async down(queryRunner: QueryRunner): Promise<void> {
    await queryRunner.dropIndex('users', 'IDX_USERS_EMAIL');
    await queryRunner.dropTable('users');
  }
}

Transactions

typescript
// Using QueryRunner
const queryRunner = AppDataSource.createQueryRunner();
await queryRunner.connect();
await queryRunner.startTransaction();

try {
  const user = queryRunner.manager.create(User, {
    email: 'user@example.com',
    name: 'User',
  });
  await queryRunner.manager.save(user);

  const post = queryRunner.manager.create(Post, {
    title: 'First Post',
    author: user,
  });
  await queryRunner.manager.save(post);

  await queryRunner.commitTransaction();
} catch (error) {
  await queryRunner.rollbackTransaction();
  throw error;
} finally {
  await queryRunner.release();
}

// Using transaction method
await AppDataSource.transaction(async manager => {
  const user = manager.create(User, {
    email: 'user@example.com',
    name: 'User',
  });
  await manager.save(user);

  const post = manager.create(Post, {
    title: 'First Post',
    author: user,
  });
  await manager.save(post);
});

NestJS Integration

typescript
// app.module.ts
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { User } from './entities/user.entity';
import { UsersModule } from './users/users.module';

@Module({
  imports: [
    TypeOrmModule.forRoot({
      type: 'postgres',
      host: 'localhost',
      port: 5432,
      username: 'user',
      password: 'password',
      database: 'db',
      entities: [User],
      synchronize: false,
    }),
    UsersModule,
  ],
})
export class AppModule {}

// users/users.module.ts
@Module({
  imports: [TypeOrmModule.forFeature([User])],
  providers: [UsersService],
  controllers: [UsersController],
})
export class UsersModule {}

// users/users.service.ts
@Injectable()
export class UsersService {
  constructor(
    @InjectRepository(User)
    private usersRepository: Repository<User>,
  ) {}

  findAll(): Promise<User[]> {
    return this.usersRepository.find();
  }

  findOne(id: number): Promise<User | null> {
    return this.usersRepository.findOneBy({ id });
  }
}

Best Practices

Use Migrations in Production

Never use synchronize: true in production. Always use migrations:

typescript
// Development: Use migrations, not sync
synchronize: false,
Eager vs Lazy Loading
typescript
// Eager loading - loads relations automatically
@OneToMany(() => Post, (post) => post.author, { eager: true })
posts: Post[];

// Lazy loading - loads relations on access
@OneToMany(() => Post, (post) => post.author)
posts: Promise<Post[]>;

// Explicit loading (recommended)
const user = await userRepository.findOne({
  where: { id: 1 },
  relations: ["posts"],
});
Avoid N+1 Queries
typescript
// Bad: N+1 queries
const users = await userRepository.find();
for (const user of users) {
  console.log(user.posts); // Separate query for each user
}

// Good: Eager load relations
const users = await userRepository.find({
  relations: ['posts'],
});
Use Indexes
typescript
@Entity()
@Index(['email'])
@Index(['firstName', 'lastName'])
export class User {
  @Column()
  @Index()
  email: string;

  @Column()
  firstName: string;

  @Column()
  lastName: string;
}
Cascade Operations
typescript
@OneToMany(() => Post, (post) => post.author, {
  cascade: true, // Saves/removes related posts
  onDelete: "CASCADE", // Database-level cascade
})
posts: Post[];
Naming Strategies

For consistent naming between TypeScript and database:

typescript
import { DefaultNamingStrategy, NamingStrategyInterface } from "typeorm";
import { snakeCase } from "typeorm/util/StringUtils";

export class SnakeNamingStrategy extends DefaultNamingStrategy implements NamingStrategyInterface {
  tableName(targetName: string, userSpecifiedName: string | undefined): string {
    return userSpecifiedName ? userSpecifiedName : snakeCase(targetName);
  }

  columnName(propertyName: string, customName: string, embeddedPrefixes: string[]): string {
    return snakeCase(embeddedPrefixes.join("_")) + (customName ? customName : snakeCase(propertyName));
  }
}

// Use in data source config
namingStrategy: new SnakeNamingStrategy(),

© rolling-scopes, MPL-2.0. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file

Files

Just SKILL.md in .agents/skills/typeorm of rolling-scopes/rsschool-app.

Open the folder on GitHubat commit f741ad2

Used in 1 other repository

We found 1 copy of this SKILL.md (exact, near-identical or edited) in other folders, from 1 other GitHub owner. This page covers the copy in rolling-scopes/rsschool-app, which our catalogue first saw on October 7, 2026.

Compare with similar skills

Typeorm next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.

Typeorm compared with similar skills
SkillStarsUsed inTokensAuto-checkLicenceRepo updated
Typeorm this skillrolling-scopes/rsschool-app10k1 repos~3.5kAutomated safety check: PassMPL-2.0
Create Auth Skilldeadlock-mod-manager/deadlock-mod-manager5744 repos~3.4kAutomated safety check: PassGPL-3.0
Twenty Entity Runner Actionstwentyhq/twenty58k—~3.1kAutomated safety check: PassCustom licence
Twenty Syncable Entity Typestwentyhq/twenty58k—~2.8kAutomated safety check: PassCustom licence
Hot Monitorliyupi/yupi-hot-monitor7181 repos~1.2kAutomated safety check: PassNone
ClickHouse RowBinary for Node.jsClickHouse/agent-skills544—~1.3kAutomated safety check: PassApache-2.0

Similar skills

  • Create Auth Skill

    deadlock-mod-manager/deadlock-mod-manager

    Scaffold and implement authentication in TypeScript/JavaScript apps using Better Auth.

    574 GitHub starsUsed in 4 repos~3.4k tokens
    DatabasesAuto-check passed
  • Step four of adding a syncable entity in the Twenty server: write create, update and delete action handlers that run workspace migrations against the database.

    58k GitHub stars~3.1k tokensUpdated today
    Backend & APIsAuto-check passed
  • Walks through the first of six steps for adding a syncable entity to Twenty's server: metadata name, TypeORM entity, flat types and central constants.

    58k GitHub stars~2.8k tokensUpdated today
    DevelopmentAuto-check passed
  • Hot Monitor

    liyupi/yupi-hot-monitor

    AI hotspot monitoring and trending topic discovery across multiple sources (Bing, Google, DuckDuckGo, HackerNews, Sogou, Bilibili, Weibo, Twitter).

    718 GitHub starsUsed in 1 repo~1.2k tokens
    DatabasesAuto-check passed
  • ClickHouse RowBinary for Node.js

    ClickHouse/agent-skills

    Generates TypeScript or JavaScript readers and writers for ClickHouse's RowBinary formats over HTTP in Node.js, after checking that RowBinary suits the data.

    544 GitHub stars~1.3k tokensUpdated 9 days ago
    DatabasesAuto-check passed
  • Drizzle Migrations

    bretzel-app/crumbs

    Drizzle ORM schema management and SQLite migrations — adding tables, modifying columns, creating indexes, generating and running migrations, Drizzle query patterns.

    127 GitHub starsUsed in 1 repo~2.6k tokens
    DatabasesAuto-check passed

More from rolling-scopes/rsschool-app

  • Typescript Advanced Types

    rolling-scopes/rsschool-app

    Master TypeScript's advanced type system including generics, conditional types, mapped types, template literals, and utility types for building type-safe applications.

    10k GitHub starsUsed in 25 repos~4.2k tokens
    Auto-check passed
  • Nestjs Best Practices

    rolling-scopes/rsschool-app

    NestJS best practices and architecture patterns for building production-ready applications.

    10k GitHub starsUsed in 6 repos~1.2k tokens
    Auto-check passed

Categories

Questions about Typeorm

What does Typeorm do?

Guidelines for developing with TypeORM, a full-featured ORM for TypeScript and JavaScript supporting multiple databases. Typeorm is an agent skill from rolling-scopes/rsschool-app.

When should I use Typeorm?

Typeorm fits situations like: tasks that involve ORMs and data access.

How do I install Typeorm in Claude Code?

Run `npx skills add rolling-scopes/rsschool-app --skill typeorm -a claude-code`. Or copy the skill folder (.agents/skills/typeorm in rolling-scopes/rsschool-app) into .claude/skills/typeorm in your project. Claude Code loads it when a task matches its description.

How do I install Typeorm in Codex?

Run `npx skills add rolling-scopes/rsschool-app --skill typeorm -a codex`. Or copy the skill folder (.agents/skills/typeorm in rolling-scopes/rsschool-app) into .agents/skills/typeorm in your project. Codex loads it when a task matches its description.

Can I use Typeorm in Cursor, Gemini CLI or GitHub Copilot?

Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add rolling-scopes/rsschool-app --skill typeorm -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/typeorm, .gemini/skills/typeorm, .github/skills/typeorm and .opencode/skills/typeorm in your project.

What does Typeorm need to run?

Going by SKILL.md and its folder, Typeorm needs the command-line tools its instructions call (npx) and credentials named DB_PASSWORD. Our summary lists: Node.js.

Does Typeorm access the network?

SKILL.md contains no URLs. Its commands use npx, which can reach the network depending on how they are called. This is read from the text; nothing was executed.

Is Typeorm safe to install?

Our automated static check of SKILL.md found no risky patterns, such as piping downloads into a shell, reading credential files or hidden Unicode. It is not a guarantee. Review the folder before installing.

What licence does Typeorm use?

Typeorm is published under the MPL-2.0 licence (the repository's licence). It allows redistribution, so the full SKILL.md is shown on this page.

How many tokens does Typeorm use?

About 3.5k tokens (SKILL.md is roughly 14k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full.

What are the alternatives to Typeorm?

Skills that share tags, products or a category with Typeorm: Create Auth Skill (deadlock-mod-manager/deadlock-mod-manager, 574 stars), Twenty Entity Runner Actions (twentyhq/twenty, 58k stars), Twenty Syncable Entity Types (twentyhq/twenty, 58k stars) and Hot Monitor (liyupi/yupi-hot-monitor, 718 stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.

Who maintains Typeorm?

rolling-scopes (a GitHub organization) maintains it in rolling-scopes/rsschool-app, which has 10,399 GitHub stars. The repository holds 3 skills in this directory. The repository was last updated on October 4, 2026.

Source: rolling-scopes/rsschool-app on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.