Skip to content

Entity_definition


An entity is a TypeScript class that maps to a database table. TypeORM uses decorators to define the mapping.

Entity to Table Mapping
================================================================================
TypeScript Entity Database Table
+------------------------+ +------------------------+
| @Entity('users') | | TABLE users |
| class User { | maps | ---------------------- |
| @PrimaryGenerated... | -----> | id SERIAL PK |
| id: number; | | name VARCHAR(100) |
| | | email VARCHAR(255) |
| @Column() | | active BOOLEAN |
| name: string; | | created TIMESTAMP |
| | +------------------------+
| @Column() | | |
| email: string; | | |
| | | |
| @Column() | | |
| active: boolean; | | |
| } | | |
+------------------------+ +------------------------+
================================================================================

The @Entity() decorator marks a class as a database entity.

import { Entity } from 'typeorm';
// Basic usage - table name = class name (lowercase)
@Entity()
class User {
// Creates table named "user"
}
// Custom table name
@Entity('users')
class User {
// Creates table named "users"
}
// With schema
@Entity('users', { schema: 'public' })
class User {
// Creates table in specific schema
}
// With database (for multi-database)
@Entity('users', { database: 'secondary' })
class User {
// Creates table in specific database
}
// Without synchronization
@Entity('users', { synchronize: false })
class User {
// Table won't be auto-synced
}
@Entity() Options
+------------------------------------------------------------------+
| |
| @Entity(name?: string, options?: EntityOptions) |
| |
| Options: |
| +----------------------------------------------------------+ |
| | | |
| | name: string | Table name | |
| | database: string | Database name | |
| | schema: string | Schema name | |
| | synchronize: boolean | Auto-sync (default: true) | |
| | orderBy: object | Default ordering | |
| | engine: string | Database engine | |
| | database: string | Database selection | |
| | | |
| +----------------------------------------------------------+ |
| |
+------------------------------------------------------------------+

Defines a primary key column.

import { Entity, PrimaryColumn } from 'typeorm';
@Entity('users')
class User {
@PrimaryColumn()
id: string;
// Generated SQL:
// id VARCHAR(255) PRIMARY KEY
}

Defines an auto-generated primary key.

import { Entity, PrimaryGeneratedColumn } from 'typeorm';
@Entity('users')
class User {
// Auto-increment integer
@PrimaryGeneratedColumn()
id: number;
// Generated SQL: id SERIAL PRIMARY KEY
// UUID
@PrimaryGeneratedColumn('uuid')
id: string;
// Generated SQL: id UUID PRIMARY KEY DEFAULT uuid_generate_v4()
// Identity (PostgreSQL 10+)
@PrimaryGeneratedColumn('identity')
id: number;
// Generated SQL: id GENERATED BY DEFAULT AS IDENTITY
}

Defines a regular column.

import { Entity, Column } from 'typeorm';
@Entity('users')
class User {
@Column()
name: string;
// Generated SQL: name VARCHAR(255)
@Column({ type: 'text' })
bio: string;
// Generated SQL: bio TEXT
@Column({ length: 100 })
shortName: string;
// Generated SQL: shortName VARCHAR(100)
@Column({ unique: true })
email: string;
// Generated SQL: email VARCHAR(255) UNIQUE
@Column({ nullable: true })
nickname: string;
// Generated SQL: nickname VARCHAR(255) NULL
@Column({ default: 'user' })
role: string;
// Generated SQL: role VARCHAR(255) DEFAULT 'user'
@Column({ select: false })
password: string;
// Column excluded from default SELECT queries
}

@Entity('posts')
class Post {
// VARCHAR (default for string)
@Column()
title: string;
// SQL: title VARCHAR(255)
// VARCHAR with length
@Column({ length: 100 })
shortTitle: string;
// SQL: shortTitle VARCHAR(100)
// TEXT
@Column({ type: 'text' })
content: string;
// SQL: content TEXT
// CHAR (fixed length)
@Column({ type: 'char', length: 2 })
countryCode: string;
// SQL: countryCode CHAR(2)
// VARCHAR array (PostgreSQL)
@Column({ type: 'varchar', array: true })
tags: string[];
// SQL: tags VARCHAR[]
}
@Entity('products')
class Product {
// INTEGER (default for number)
@Column()
quantity: number;
// SQL: quantity INTEGER
// INT
@Column({ type: 'int' })
stock: number;
// SQL: stock INT
// BIGINT
@Column({ type: 'bigint' })
largeNumber: number;
// SQL: largeNumber BIGINT
// SMALLINT
@Column({ type: 'smallint' })
smallNumber: number;
// SQL: smallNumber SMALLINT
// DECIMAL
@Column({ type: 'decimal', precision: 10, scale: 2 })
price: number;
// SQL: price DECIMAL(10, 2)
// FLOAT
@Column({ type: 'float' })
rating: number;
// SQL: rating FLOAT
// DOUBLE
@Column({ type: 'double' })
preciseRating: number;
// SQL: preciseRating DOUBLE
// REAL
@Column({ type: 'real' })
anotherNumber: number;
// SQL: anotherNumber REAL
}
@Entity('users')
class User {
@Column()
isActive: boolean;
// SQL: isActive BOOLEAN DEFAULT false
@Column({ default: true })
emailVerified: boolean;
// SQL: emailVerified BOOLEAN DEFAULT true
}
@Entity('events')
class Event {
// TIMESTAMP (default for Date)
@Column()
createdAt: Date;
// SQL: createdAt TIMESTAMP
// DATE
@Column({ type: 'date' })
birthDate: Date;
// SQL: birthDate DATE
// TIME
@Column({ type: 'time' })
startTime: Date;
// SQL: startTime TIME
// DATETIME (MySQL)
@Column({ type: 'datetime' })
updatedAt: Date;
// SQL: updatedAt DATETIME
// TIMESTAMP WITH TIMEZONE
@Column({ type: 'timestamptz' })
eventTime: Date;
// SQL: eventTime TIMESTAMPTZ
}
@Entity('users')
class User {
// JSON
@Column({ type: 'json' })
preferences: object;
// SQL: preferences JSON
// JSONB (PostgreSQL - binary JSON, faster)
@Column({ type: 'jsonb' })
metadata: Record<string, any>;
// SQL: metadata JSONB
// Simple JSON
@Column({ type: 'simple-json' })
tags: string[];
// SQL: tags TEXT (stored as JSON string)
}
@Entity('posts')
class Post {
// Integer array
@Column({ type: 'int', array: true })
ratings: number[];
// SQL: ratings INT[]
// Text array
@Column({ type: 'text', array: true })
tags: string[];
// SQL: tags TEXT[]
// UUID array
@Column({ type: 'uuid', array: true })
relatedIds: string[];
// SQL: relatedIds UUID[]
}
enum UserRole {
ADMIN = 'admin',
USER = 'user',
GUEST = 'guest',
}
enum UserStatus {
ACTIVE,
INACTIVE,
PENDING,
}
@Entity('users')
class User {
// String enum
@Column({
type: 'enum',
enum: UserRole,
default: UserRole.USER,
})
role: UserRole;
// SQL: role ENUM('admin', 'user', 'guest') DEFAULT 'user'
// Numeric enum
@Column({
type: 'enum',
enum: UserStatus,
default: UserStatus.PENDING,
})
status: UserStatus;
// SQL: status INT DEFAULT 2 (enum values as numbers)
// Enum with custom name
@Column({
type: 'enum',
enum: UserRole,
enumName: 'user_role_enum',
})
customRole: UserRole;
// SQL: Creates custom enum type in PostgreSQL
}

Automatically sets the date when entity is created.

@Entity('users')
class User {
@CreateDateColumn()
createdAt: Date;
// Automatically set to current date on INSERT
}

Automatically updates the date when entity is updated.

@Entity('users')
class User {
@UpdateDateColumn()
updatedAt: Date;
// Automatically updated on each UPDATE
}

Used for soft deletes.

@Entity('users')
class User {
@DeleteDateColumn()
deletedAt: Date;
// Set to current date when entity is "deleted"
// NULL means not deleted
}

Used for optimistic locking.

@Entity('users')
class User {
@VersionColumn()
version: number;
// Automatically incremented on each UPDATE
}

Complete Column Options
+------------------------------------------------------------------+
| |
| @Column({ |
| type: 'varchar', // Column type |
| length: 255, // String length |
| precision: 10, // Numeric precision |
| scale: 2, // Numeric scale |
| nullable: false, // Allow NULL? |
| default: 'value', // Default value |
| unique: false, // Unique constraint |
| select: true, // Include in SELECT? |
| insert: true, // Include in INSERT? |
| update: true, // Include in UPDATE? |
| readonly: false, // Never update? |
| comment: 'Description', // Column comment |
| array: false, // Is array? (PostgreSQL) |
| enum: [], // Enum values |
| enumName: '', // Enum type name (PostgreSQL) |
| transformer: { // Value transformer |
| to: (value) => value, |
| from: (value) => value, |
| }, |
| name: 'column_name', // Custom column name |
| primary: false, // Is primary key? |
| generated: false, // Auto-generate? |
| collation: 'utf8mb4', // Collation |
| charset: 'utf8mb4', // Character set |
| spatialFeatureType: '', // Spatial type |
| srid: 0, // Spatial reference ID |
| }) |
| |
+------------------------------------------------------------------+

src/entities/user.entity.ts
import {
Entity,
PrimaryGeneratedColumn,
Column,
CreateDateColumn,
UpdateDateColumn,
DeleteDateColumn,
Index,
} from 'typeorm';
enum UserRole {
ADMIN = 'admin',
USER = 'user',
MODERATOR = 'moderator',
}
@Entity('users')
@Index(['email'], { unique: true }) // Unique index on email
@Index(['firstName', 'lastName']) // Composite index
export class User {
// Primary Key
@PrimaryGeneratedColumn('uuid')
id: string;
// Basic columns
@Column({ length: 50 })
firstName: string;
@Column({ length: 50 })
lastName: string;
@Column({ unique: true })
email: string;
@Column({ select: false }) // Excluded from default SELECT
password: string;
// With default value
@Column({ default: true })
isActive: boolean;
// Enum column
@Column({
type: 'enum',
enum: UserRole,
default: UserRole.USER,
})
role: UserRole;
// Nullable column
@Column({ type: 'text', nullable: true })
bio: string;
// JSON column
@Column({ type: 'jsonb', nullable: true })
preferences: Record<string, any>;
// Date columns
@CreateDateColumn({ name: 'created_at' })
createdAt: Date;
@UpdateDateColumn({ name: 'updated_at' })
updatedAt: Date;
// Soft delete
@DeleteDateColumn({ name: 'deleted_at', nullable: true })
deletedAt: Date;
// Computed property (not a column)
get fullName(): string {
return `${this.firstName} ${this.lastName}`;
}
}

Single Table Inheritance
+------------------------------------------------------------------+
| |
| All entities stored in ONE table with discriminator column |
| |
| +----------------------------------------------------------+ |
| | content_items | |
| |----------------------------------------------------------| |
| | id | type | title | content | duration | url | |
| |----|----------|----------|----------|----------|---------| |
| | 1 | article | News | Text... | NULL | NULL | |
| | 2 | video | Tutorial | NULL | 120 | http:// | |
| | 3 | article | Guide | Text... | NULL | NULL | |
| +----------------------------------------------------------+ |
| |
+------------------------------------------------------------------+
import { Entity, Column, TableInheritance, ChildEntity } from 'typeorm';
// Base entity
@Entity('content_items')
@TableInheritance({
column: { name: 'type', type: 'varchar', length: 20 },
})
export abstract class ContentItem {
@PrimaryGeneratedColumn()
id: number;
@Column()
title: string;
}
// Child entities
@ChildEntity('article')
export class Article extends ContentItem {
@Column()
content: string;
}
@ChildEntity('video')
export class Video extends ContentItem {
@Column()
duration: number;
@Column()
url: string;
}
import { Entity, Column, TableInheritance, ChildEntity, PrimaryGeneratedColumn } from 'typeorm';
// Base entity
@Entity()
@TableInheritance({
pattern: 'class-table',
})
export abstract class Person {
@PrimaryGeneratedColumn()
id: number;
@Column()
name: string;
}
// Child entities (each has own table)
@ChildEntity()
@Entity('employees')
export class Employee extends Person {
@Column()
salary: number;
@Column()
department: string;
}
@ChildEntity()
@Entity('customers')
export class Customer extends Person {
@Column()
email: string;
@Column()
totalPurchases: number;
}
import { Entity, Column, Embedded } from 'typeorm';
// Embedded entity (not a table)
export class Address {
@Column()
street: string;
@Column()
city: string;
@Column()
country: string;
@Column()
postalCode: string;
}
export class Profile {
@Column()
bio: string;
@Column()
avatar: string;
}
// Main entity with embedded entities
@Entity('users')
export class User {
@PrimaryGeneratedColumn()
id: number;
@Column()
name: string;
// Embedded entity - columns prefixed with 'address_'
@Embedded(() => Address, { prefix: 'address' })
address: Address;
// Embedded entity - columns prefixed with 'profile_'
@Embedded(() => Profile, { prefix: 'profile' })
profile: Profile;
}
// Generated table:
// CREATE TABLE users (
// id SERIAL PRIMARY KEY,
// name VARCHAR(255),
// address_street VARCHAR(255),
// address_city VARCHAR(255),
// address_country VARCHAR(255),
// address_postalCode VARCHAR(255),
// profile_bio VARCHAR(255),
// profile_avatar VARCHAR(255)
// );

import { Entity, Index, Column, PrimaryGeneratedColumn } from 'typeorm';
@Entity('users')
@Index(['firstName', 'lastName']) // Composite index
@Index(['email'], { unique: true }) // Unique index
export class User {
@PrimaryGeneratedColumn()
id: number;
@Column()
firstName: string;
@Column()
lastName: string;
@Column()
email: string;
@Index() // Column-level index
@Column()
username: string;
}
@Entity('posts')
@Index(['title', 'content'], {
synchronize: true, // Auto-create index
name: 'IDX_POST_SEARCH', // Custom index name
unique: false, // Unique constraint
spatial: false, // Spatial index
fulltext: true, // Full-text index (MySQL)
where: 'deleted_at IS NULL', // Partial index (PostgreSQL)
})
export class Post {
@PrimaryGeneratedColumn()
id: number;
@Column()
title: string;
@Column({ type: 'text' })
content: string;
@DeleteDateColumn()
deletedAt: Date;
}

TypeORM uses naming strategies to convert entity/property names to table/column names.

// Snake case naming strategy
import { DefaultNamingStrategy, NamingStrategyInterface } from 'typeorm';
import { camelCase, snakeCase } from 'typeorm/util/StringUtils';
export class SnakeNamingStrategy
extends DefaultNamingStrategy
implements NamingStrategyInterface
{
tableName(className: string, customName: string): string {
return customName || snakeCase(className);
}
columnName(
propertyName: string,
customName: string,
embeddedPrefix: string[]
): string {
return snakeCase(embeddedPrefix.join('_')) + (customName || snakeCase(propertyName));
}
relationName(propertyName: string): string {
return snakeCase(propertyName);
}
}
// Usage in DataSource
import { DataSource } from 'typeorm';
import { SnakeNamingStrategy } from './naming-strategies/snake-naming.strategy';
export const AppDataSource = new DataSource({
// ... other options
namingStrategy: new SnakeNamingStrategy(),
});
Naming Strategy Comparison
+------------------------------------------------------------------+
| |
| TypeScript Name | Default Strategy | Snake Strategy |
| ---------------------|------------------|---------------------- |
| class UserAccount | useraccount | user_account |
| firstName | firstName | first_name |
| lastName | lastName | last_name |
| createdAt | createdAt | created_at |
| isActive | isActive | is_active |
| |
+------------------------------------------------------------------+

import { getMetadataArgsStorage } from 'typeorm';
// Get all entity metadata
const metadata = getMetadataArgsStorage();
// Get tables
console.log(metadata.tables);
// Output: [{ target: User, name: 'users', ... }]
// Get columns
console.log(metadata.columns);
// Output: [{ target: User, propertyName: 'firstName', ... }]
// Get indices
console.log(metadata.indices);
// Output: [{ target: User, columns: ['email'], ... }]

Entity Decorators Summary
+------------------------------------------------------------------+
| |
| Decorator | Purpose |
| ------------------------|-------------------------------------- |
| @Entity() | Mark class as database table |
| @PrimaryColumn() | Primary key column |
| @PrimaryGeneratedColumn()| Auto-generated primary key |
| @Column() | Regular column |
| @CreateDateColumn() | Auto-set on create |
| @UpdateDateColumn() | Auto-update on modify |
| @DeleteDateColumn() | Soft delete timestamp |
| @VersionColumn() | Optimistic locking version |
| @Index() | Create index |
| @Embedded() | Embed another entity |
| @TableInheritance() | Enable inheritance |
| @ChildEntity() | Mark as child entity |
| |
+------------------------------------------------------------------+

Chapter 7: Column Types & Options


Last Updated: February 2026