Skip to content
Back to skills

Typeorm

ASecurity

TypeORM is an ORM for TypeScript and JavaScript that maps decorated classes to tables in PostgreSQL, MySQL, MariaDB, SQLite, SQL Server, Oracle and other databases. Use when a user asks to define entities and relations, query with repositories or QueryBuilder, set up a DataSource, generate and run migrations, or upgrade a project from TypeORM 0.3 to 1.x — for example "add a column with a migration", "migration:generate cannot open my data-source.ts", or "findOneBy throws on an undefined value...

  • 142 stars
  • 0 votes
  • 0 copies
  • 2 views
  • Added September 6, 2026
databasesjavascripttypescriptgojavabashsqlnodegitapidatabase

Works with

  • terminal
  • cli
  • api

Security analysis

A96/100
  • mediumInstalls packages at runtime which could introduce malicious dependencies

Pro scans all 2 files and shows the line behind each finding

Scanned October 4, 2026

npx -y skills add TerminalSkills/skills --skill typeorm --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Typeorm?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Typeorm
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/terminalskills-typeorm/badge)](https://www.skillsdirectory.com/skills/terminalskills-typeorm)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: typeorm
description: >-
  TypeORM is an ORM for TypeScript and JavaScript that maps decorated classes
  to tables in PostgreSQL, MySQL, MariaDB, SQLite, SQL Server, Oracle and
  other databases. Use when a user asks to define entities and relations,
  query with repositories or QueryBuilder, set up a DataSource, generate and
  run migrations, or upgrade a project from TypeORM 0.3 to 1.x — for example
  "add a column with a migration", "migration:generate cannot open my
  data-source.ts", or "findOneBy throws on an undefined value after the
  upgrade".
license: Apache-2.0
compatibility: "TypeORM 1.x needs Node.js 20.19+, 22.13+ or 24.11+ and TypeScript 4.5+ with experimentalDecorators and emitDecoratorMetadata"
metadata:
  author: terminal-skills
  version: "1.1.0"
  category: development
  repository: https://github.com/typeorm/typeorm
  tags: ["orm", "typescript", "database", "sql", "migrations"]
---

# TypeORM — TypeScript ORM for SQL Databases

## Overview

TypeORM maps TypeScript classes to database tables with decorators and gives you repositories, a QueryBuilder, transactions and a migration CLI on top. It supports PostgreSQL, MySQL/MariaDB, SQLite, SQL Server, Oracle, CockroachDB, SAP HANA, Spanner and MongoDB. This skill targets TypeORM 1.x (1.0 shipped in May 2026 and removed the APIs deprecated during 0.3); the 0.3 line still gets fixes under the npm `legacy` tag.

## Instructions

### Install and configure

```bash
npm install typeorm reflect-metadata
npm install pg                                    # driver: pg, mysql2, better-sqlite3, mssql, oracledb or mongodb
npm install -D typescript@5 ts-node @types/node   # ts-node runs the CLI against .ts files
npx typeorm init --name blog-api --database postgres   # or scaffold: package.json, tsconfig.json, src/data-source.ts, src/entities/User.ts
```

`tsconfig.json` must set `"experimentalDecorators": true` and `"emitDecoratorMetadata": true`. All access goes through one `DataSource` instance — the global `createConnection()` / `getRepository()` helpers no longer exist.

```typescript
// src/data-source.ts
import "reflect-metadata";
import { DataSource } from "typeorm";
import { User, Post, Tag } from "./entities";   // one file or an index re-exporting each entity

export const AppDataSource = new DataSource({
  type: "postgres",
  url: process.env.DATABASE_URL,
  entities: [User, Post, Tag],
  migrations: [__dirname + "/migrations/*{.ts,.js}"],
  synchronize: false,          // schema changes go through migrations
  poolSize: 20,                // max connections in the pool
});
// At startup: await AppDataSource.initialize();   On shutdown: await AppDataSource.destroy();
```

### Entity Definition

```typescript
import { Entity, PrimaryGeneratedColumn, Column, CreateDateColumn, UpdateDateColumn,
  ManyToOne, OneToMany, ManyToMany, JoinTable, Index, BeforeInsert } from "typeorm";

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

  @Column({ length: 100 })
  name: string;

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

  @Column({ select: false })
  passwordHash: string;

  @Column({ type: "enum", enum: ["user", "admin"], default: "user" })
  role: "user" | "admin";

  @Column({ type: "jsonb", nullable: true })
  profile: { bio?: string; avatar?: string };

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

  @ManyToMany(() => Tag)
  @JoinTable()
  interests: Tag[];

  @CreateDateColumn()
  createdAt: Date;

  @UpdateDateColumn()
  updatedAt: Date;

  @BeforeInsert()
  normalizeEmail() {
    this.email = this.email.toLowerCase().trim();
  }
}

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

  @Column()
  title: string;

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

  @Column({ default: false })
  published: boolean;

  @ManyToOne(() => User, (user) => user.posts)
  author: User;

  @Column()
  authorId: string;

  @CreateDateColumn()
  createdAt: Date;
}

@Entity("tags")
export class Tag { @PrimaryGeneratedColumn() id: number; @Column({ unique: true }) name: string; }
```

### Repositories and find options

```typescript
import { In, IsNull } from "typeorm";
import { AppDataSource as dataSource } from "./data-source";
const users = dataSource.getRepository(User);
// select and relations take objects — the string-array form was removed in 1.0
const admins = await users.find({
  select: { id: true, name: true, posts: { id: true, title: true } },
  relations: { posts: true },
  where: { role: "admin" },
  order: { name: "ASC" },     // with take + relations, order by a column that is in select
  take: 20,
});
const maya = await users.findOneBy({ email: "maya.okafor@northwind.dev" }); // replaces findOneById
const picked = await users.findBy({ id: In(userIds) });                     // replaces findByIds
const noProfile = await users.find({ where: { profile: IsNull() } });       // `profile: null` throws
const hasAdmins = await users.exists({ where: { role: "admin" } });         // replaces exist()
await users.upsert(
  { name: "Maya Okafor", email: "maya.okafor@northwind.dev", passwordHash: hash },
  ["email"],                                                                // conflict target
);
// Custom repository methods: extend() replaces @EntityRepository / getCustomRepository
const UserRepository = users.extend({
  findByEmail(email: string) {
    return this.findOneBy({ email: email.toLowerCase().trim() });
  },
});
```

### QueryBuilder

```typescript
const posts = await dataSource
  .getRepository(Post)
  .createQueryBuilder("post")
  .leftJoinAndSelect("post.author", "author")
  .where("post.published = :published", { published: true })
  .andWhere("author.role = :role", { role: "admin" })
  .orderBy("post.createdAt", "DESC")
  .skip(20)
  .take(10)
  .getMany();

// Subquery
const topAuthors = await dataSource
  .getRepository(User)
  .createQueryBuilder("user")
  .addSelect((subQuery) =>
    subQuery
      .select("COUNT(post.id)", "postCount")
      .from(Post, "post")
      .where("post.authorId = user.id"),
    "postCount"
  )
  .orderBy("postCount", "DESC")
  .limit(10)
  .getRawMany();

// A select: false column is only loaded when asked for
const withHash = await dataSource.getRepository(User).createQueryBuilder("user")
  .addSelect("user.passwordHash").where("user.email = :email", { email: loginEmail }).getOne();

// Transactions
await dataSource.transaction(async (manager) => {
  const user = manager.create(User, { name: "Maya Okafor", email: "maya.okafor@northwind.dev", passwordHash: hash });
  await manager.save(user);
  const post = manager.create(Post, { title: "Zero-downtime migrations", body: draft, author: user });
  await manager.save(post);
});
```

### Migrations

Plain `npx typeorm` only loads JavaScript. With a `.ts` data source, use the bundled ts-node wrapper (`typeorm-ts-node-esm` in ESM projects), or compile and point `-d` at the built file.

```bash
# Generate a migration from the difference between entities and the database
npx typeorm-ts-node-commonjs migration:generate src/migrations/AddPostSlug -d src/data-source.ts
# Create an empty migration to fill in by hand
npx typeorm migration:create src/migrations/BackfillPostSlugs
# Run pending migrations, list their status, revert the last one
npx typeorm-ts-node-commonjs migration:run -d src/data-source.ts
npx typeorm-ts-node-commonjs migration:show -d src/data-source.ts
npx typeorm-ts-node-commonjs migration:revert -d src/data-source.ts
# CI guard: exit code 1 when entities and schema have drifted, nothing is written
npx typeorm-ts-node-commonjs migration:generate src/migrations/Drift --check -d src/data-source.ts
# Production: run the compiled output (dist/ = your tsconfig outDir; `typeorm init` sets ./build)
npx tsc && npx typeorm migration:run -d dist/data-source.js
```

### Upgrading from 0.3 to 1.x

Run the official codemod first (`npx @typeorm/codemod v1 --dry src/` to preview, then without `--dry`). It rewrites what it can and leaves `TODO(typeorm-v1)` comments where a decision is needed. The changes that break most projects:

| 0.3 | 1.x |
|-----|-----|
| `Connection`, `createConnection()`, global `getRepository()` / `getManager()` | `DataSource`, `dataSource.initialize()`, `dataSource.getRepository()` |
| `findOneById(id)`, `findByIds(ids)`, `exist()` | `findOneBy({ id })`, `findBy({ id: In(ids) })`, `exists()` |
| `select: ["id"]`, `relations: ["posts"]` | `select: { id: true }`, `relations: { posts: true }` |
| `where: { col: null }` or `undefined` was ignored | throws `TypeORMError`; use `IsNull()` or set `invalidWhereValuesBehavior` |
| `type: "sqlite"` (`sqlite3`), `mysql` package | `type: "better-sqlite3"`, `mysql2` |
| `@EntityRepository`, `getCustomRepository()` | `repository.extend({...})` |
| `TYPEORM_*` environment variables, `ormconfig.env` | a data source file that reads `process.env` itself |
| `qb.printSql()`, `qb.onConflict()` | `qb.getSql()`, `orIgnore()` / `orUpdate()` |

NestJS projects need `@nestjs/typeorm` 11.0.1 or later. Full list: https://typeorm.io/docs/releases/1.0/upgrading-from-0.3

## Examples

### Example 1: Add a column and ship it as a migration

**User request:** "Add an optional slug to posts and create the migration for it."

Add the column to the `Post` entity, then generate and apply the migration:

```typescript
@Column({ type: "varchar", length: 160, nullable: true })
slug: string | null;
```

```bash
npx typeorm-ts-node-commonjs migration:generate src/migrations/AddPostSlug -d src/data-source.ts
npx typeorm-ts-node-commonjs migration:run -d src/data-source.ts
```

The first command writes `src/migrations/1790847921068-AddPostSlug.ts` (the prefix is the current timestamp) whose `up()` runs `ALTER TABLE "posts" ADD "slug" character varying(160)` and whose `down()` drops the column. The second prints `Migration AddPostSlug1790847921068 has been executed successfully.`, and `migration:show` then lists it as `[X]`. If nothing changed, generate reports `No changes in database schema were found`.

### Example 2: Upgrade a 0.3 service to TypeORM 1.x

**User request:** "Bump our billing service from typeorm 0.3.20 to the current version."

```bash
npx @typeorm/codemod v1 --dry src/     # lists the transforms that would apply
npx @typeorm/codemod v1 src/
npm install
```

The codemod turns this:

```typescript
const repo = getRepository(Invoice);
const one = await repo.findOneById(invoiceId);
const list = await repo.find({ select: ["id", "total"], relations: ["customer"] });
```

into this, and bumps `typeorm` to `^1.0.0`, `@nestjs/typeorm` to `^11.0.1` and replaces `sqlite3` with `better-sqlite3` in `package.json`:

```typescript
// TODO(typeorm-v1): `dataSource` is not defined — inject or import your DataSource instance
const repo = dataSource.getRepository(Invoice);
const one = await repo.findOneBy({ id: invoiceId });
const list = await repo.find({ select: { id: true, total: true }, relations: { customer: true } });
```

Resolve each `TODO(typeorm-v1)` by hand and grep for leftover `findOneById(`, `findByIds(` and `.exist(` — the codemod skips them in a file whose only `typeorm` import was a removed global helper. Then run the test suite and look for `Undefined value encountered in property … of a where condition` — every hit is a query that used to return unfiltered rows.

## Guidelines

1. **Migrations over sync** — Never use `synchronize: true` in production; it can drop columns. Generate migrations and read the SQL before running it.
2. **Guard where values** — In 1.x a `null` or `undefined` in find options throws instead of matching every row. Validate ids before querying; do not switch `invalidWhereValuesBehavior` back to `ignore` just to silence the error.
3. **QueryBuilder for complex queries** — Use repositories for simple CRUD, QueryBuilder for joins, subqueries and aggregations. Always bind values with `:name` parameters, never string interpolation.
4. **Select only needed fields** — Use object `select` or `.select(["user.id", "user.name"])` to avoid fetching large columns.
5. **Load relations explicitly** — Relations are not loaded unless you ask (`relations: { posts: true }` or a join). Avoid `eager: true` on large graphs; Promise-based lazy relations are documented as experimental.
6. **Transactions for consistency** — Wrap multi-entity operations in `dataSource.transaction()` and use the `manager` it passes in, not the outer repositories.
7. **`nullable: false` relations join with INNER JOIN in 1.x** — rows whose foreign key points nowhere disappear from results; fix the data or mark the relation nullable.
8. **CLI and TypeScript version** — The `typeorm-ts-node-*` wrappers depend on ts-node, which crashes under TypeScript 7; keep TypeScript 5 for the CLI (what `typeorm init` pins) or run the CLI against compiled JavaScript.
9. **Connection pooling** — Set `poolSize` to match expected concurrency; driver-specific settings go in `extra`.

Files in this skill

  • SKILL.md4.7 KB
  • _scores.json1.6 KB

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…