What is SQL to TypeORM Entity Converter?
TypeORM is the most widely used ORM in the Node.js and TypeScript ecosystem, and it is the default ORM bundled with the NestJS framework. Unlike schema-first tools such as Prisma — where a separate .prisma file is the source of truth — TypeORM is code-first and decorator-driven: your entity is a plain TypeScript class annotated with decorators like @Entity, @Column, @PrimaryGeneratedColumn, and @ManyToOne. The class itself defines both the runtime shape of your data and the database schema, and TypeORM reads those decorators at startup (via reflect-metadata) to build the schema and generate SQL. One of TypeORM's defining features is that it supports two distinct persistence patterns. In the Active Record pattern, your entity extends BaseEntity and you call methods directly on the model: User.find(), user.save(), user.remove(). In the Data Mapper pattern, you keep entities as pure data and perform all persistence through a Repository obtained from the DataSource: dataSource.getRepository(User).find(). NestJS projects almost always use the Data Mapper pattern with @InjectRepository, while smaller scripts often prefer Active Record for brevity. The same entity class works with both patterns; only how you query changes. Compared to Prisma, TypeORM gives you more direct control over decorators, lazy/eager relation loading, and custom column options, but it does not generate a fully type-safe query client the way Prisma does — its query builder and find options are typed but looser. When you are migrating an existing SQL database to a NestJS or Express + TypeORM project, this converter turns your CREATE TABLE DDL into the decorated entity classes so you do not have to hand-translate every column type and constraint.
How to Use
- Select your database dialect (PostgreSQL, MySQL, or SQLite). This controls both the TypeORM column "type" option string and how SERIAL vs AUTO_INCREMENT primary keys are emitted.
- Paste your SQL CREATE TABLE statements. Each table becomes one TypeScript class annotated with @Entity("table_name").
- Click "Generate TypeORM Entity" to produce the decorated entity classes with @Column, @PrimaryGeneratedColumn, and relation decorators.
- Save each class into its own file under src/entity/ (TypeORM convention is one entity per file, e.g. User.ts), and register them in your DataSource entities array or set entities: ["src/entity/**/*.ts"].
- Ensure "emitDecoratorMetadata": true and "experimentalDecorators": true are set in tsconfig.json, and import "reflect-metadata" at the top of your entry file — without these, decorators silently fail.
Why Use This Tool?
Tips & Best Practices
- Always add the inverse @OneToMany side manually. The converter generates the @ManyToOne owning side (the table that holds the FK column), but the parent entity needs posts: Post[] with @OneToMany(() => Post, post => post.user) for the relation to be queryable from both directions.
- Beware nullable inference: in TypeORM a column is NOT NULL by default, but a TypeScript property is non-optional by default too. For a nullable SQL column you must set both @Column({ nullable: true }) AND make the TS type string | null, otherwise strict mode complains and migrations generate the wrong DDL.
- Do not rely on synchronize: true in production. It is convenient in development but will drop and recreate columns to match entities, causing data loss. Use typeorm migration:generate to produce reviewable migration files instead.
- For DECIMAL/NUMERIC columns, pass precision and scale explicitly: @Column("decimal", { precision: 10, scale: 2 }). TypeORM returns decimals as strings in JavaScript to avoid float precision loss — do not assume you get a number.
- PostgreSQL enum columns map best to @Column({ type: "enum", enum: MyEnum }) using a TypeScript enum; a plain VARCHAR with check constraints will not give you type-safe enum values, so convert those manually after generation.
Frequently Asked Questions
What is the complete SQL type to TypeORM type mapping?
PostgreSQL: VARCHAR/CHAR → varchar/char (string), TEXT → text (string), INTEGER/INT → int (number), SMALLINT → smallint, BIGINT → bigint (string in JS), SERIAL → @PrimaryGeneratedColumn, BOOLEAN → boolean, NUMERIC/DECIMAL → decimal (string), REAL/DOUBLE PRECISION → float/double (number), TIMESTAMP/TIMESTAMPTZ → timestamp/timestamptz (Date), DATE → date, JSON/JSONB → json/jsonb (object), UUID → uuid (string), BYTEA → bytea (Buffer). MySQL: VARCHAR → varchar, INT → int, TINYINT(1) → boolean, BIGINT → bigint, DATETIME/TIMESTAMP → datetime/timestamp, JSON → json, DECIMAL → decimal, BLOB → blob (Buffer), ENUM → enum. SQLite: stores everything in TEXT/INTEGER/REAL/BLOB affinities, so varchar/int/datetime are normalized accordingly and boolean is stored as integer 0/1.
What is the difference between Active Record and Data Mapper in TypeORM?
Active Record: the entity extends BaseEntity, so persistence methods live on the model itself — const user = User.create({ name }); await user.save(); const all = await User.find(). It is concise and good for small apps. Data Mapper: entities are plain classes with no persistence methods; you go through a Repository — const repo = dataSource.getRepository(User); await repo.save(repo.create({ name })). Data Mapper is the recommended pattern for larger apps and is what NestJS uses via @InjectRepository(User). The same converted entity class supports both; you only choose how you query it.
How does TypeORM compare to Prisma?
TypeORM is code-first with decorators and supports both Active Record and Data Mapper; Prisma is schema-first with a generated, fully type-safe client. Prisma generally gives stronger end-to-end type safety and a nicer migration workflow, while TypeORM gives finer control over decorators, lazy loading, listeners/subscribers, and custom repositories, and integrates natively with NestJS dependency injection. Choose TypeORM if you are on NestJS or want decorator-based entities; choose Prisma if maximum query type safety is the priority.
How are foreign keys converted, and what is @JoinColumn for?
A SQL line like user_id INTEGER REFERENCES users(id) becomes a @ManyToOne(() => User) relation plus @JoinColumn({ name: "user_id" }). @ManyToOne tells TypeORM the relation cardinality; @JoinColumn specifies which physical column stores the foreign key (here user_id) and sits on the owning side. Without @JoinColumn, TypeORM would invent a column name like userId. Add @OneToMany(() => Post, post => post.user) on the User entity for the reverse direction.
Why are my decorators being ignored at runtime?
TypeORM decorators depend on TypeScript metadata emission. You must set "experimentalDecorators": true and "emitDecoratorMetadata": true in tsconfig.json, install reflect-metadata, and import "reflect-metadata" exactly once at the very top of your application entry point before any entity is imported. If those are missing, @Column types are not registered and you get errors like "Cannot read property of undefined" or columns missing from the generated schema.
Is my SQL data sent to a server?
No. All conversion runs entirely in your browser. Your SQL schema never leaves your device.
Real-world Examples
Single table: converting a users table to a TypeORM entity
A standalone users table with an auto-incrementing primary key, a unique email, a nullable column, a boolean with a default, and timestamps. This shows the baseline decorator mapping including @PrimaryGeneratedColumn and nullable handling.
CREATE TABLE users ( id SERIAL PRIMARY KEY, email VARCHAR(255) UNIQUE NOT NULL, display_name VARCHAR(100) NOT NULL, bio TEXT, is_active BOOLEAN NOT NULL DEFAULT true, created_at TIMESTAMP NOT NULL DEFAULT NOW() );
import { Entity, PrimaryGeneratedColumn, Column } from "typeorm";
@Entity("users")
export class User {
@PrimaryGeneratedColumn()
id: number;
@Column({ type: "varchar", length: 255, unique: true })
email: string;
@Column({ type: "varchar", length: 100 })
displayName: string;
@Column({ type: "text", nullable: true })
bio: string | null;
@Column({ type: "boolean", default: true })
isActive: boolean;
@Column({ type: "timestamp", default: () => "CURRENT_TIMESTAMP" })
createdAt: Date;
}Related tables: posts referencing users with a @ManyToOne relation
A posts table with a foreign key to users. This demonstrates how REFERENCES becomes @ManyToOne + @JoinColumn on the owning side, and why you must add the inverse @OneToMany on the User entity by hand.
CREATE TABLE posts ( id SERIAL PRIMARY KEY, user_id INTEGER NOT NULL REFERENCES users(id), title VARCHAR(255) NOT NULL, body TEXT, published BOOLEAN NOT NULL DEFAULT false, view_count INTEGER NOT NULL DEFAULT 0 );
import { Entity, PrimaryGeneratedColumn, Column, ManyToOne, JoinColumn, OneToMany } from "typeorm";
import { User } from "./User";
@Entity("posts")
export class Post {
@PrimaryGeneratedColumn()
id: number;
@Column({ name: "user_id" })
userId: number;
@ManyToOne(() => User, (user) => user.posts)
@JoinColumn({ name: "user_id" })
user: User;
@Column({ type: "varchar", length: 255 })
title: string;
@Column({ type: "text", nullable: true })
body: string | null;
@Column({ type: "boolean", default: false })
published: boolean;
@Column({ type: "int", default: 0 })
viewCount: number;
}
// Add the inverse side to User.ts manually:
// @OneToMany(() => Post, (post) => post.user)
// posts: Post[];