Data-Mapper ORM for TypeScript and ES2021+ supporting MySQL/MariaDB, PostgreSQL, MS SQL Server, Oracle, SAP HANA, SQLite, MongoDB databases.
Code-first entity definition system allowing dynamic entity creation without decorators, ideal for runtime schema generation and applications where entity structure is determined at runtime.
Core class for defining entities programmatically without using decorator syntax.
/**
* Programmatic entity definition without decorators
* @template T - Entity type
*/
class EntitySchema<T = any> {
/**
* Creates new entity schema
* @param options - Schema definition options
*/
constructor(options: EntitySchemaOptions<T>);
/** Schema configuration options */
readonly options: EntitySchemaOptions<T>;
}
/**
* Configuration options for entity schema
*/
interface EntitySchemaOptions<T> {
/** Entity name */
name: string;
/** Target constructor function (optional) */
target?: Function;
/** Database table name */
tableName?: string;
/** Database name */
database?: string;
/** Schema name */
schema?: string;
/** Column definitions */
columns: { [P in keyof T]: EntitySchemaColumnOptions };
/** Relation definitions */
relations?: { [key: string]: EntitySchemaRelationOptions };
/** Index definitions */
indices?: EntitySchemaIndexOptions[];
/** Unique constraint definitions */
uniques?: EntitySchemaUniqueOptions[];
/** Check constraint definitions */
checks?: EntitySchemaCheckOptions[];
/** Exclusion constraint definitions */
exclusions?: EntitySchemaExclusionOptions[];
/** Table inheritance configuration */
inheritancePattern?: "STI" | "CTI";
/** Discriminator column (for STI) */
discriminatorColumn?: string;
/** Default ordering */
orderBy?: Record<string, "ASC" | "DESC">;
/** Table engine (MySQL) */
engine?: string;
/** Synchronization flag */
synchronize?: boolean;
}Detailed column configuration for entity schemas.
/**
* Column definition options for entity schema
*/
interface EntitySchemaColumnOptions {
/** Column database type */
type: ColumnType;
/** Column name in database */
name?: string;
/** Column length (for string types) */
length?: string | number;
/** Column precision (for decimal types) */
precision?: number;
/** Column scale (for decimal types) */
scale?: number;
/** Whether column allows NULL */
nullable?: boolean;
/** Default column value */
default?: any;
/** Whether column is primary key */
primary?: boolean;
/** Whether column is unique */
unique?: boolean;
/** Column comment */
comment?: string;
/** Whether column is generated */
generated?: true | "increment" | "uuid" | "rowid";
/** Whether column is select by default */
select?: boolean;
/** Whether column participates in inserts */
insert?: boolean;
/** Whether column participates in updates */
update?: boolean;
/** Column collation */
collation?: string;
/** Column charset */
charset?: string;
/** Value transformer */
transformer?: ValueTransformer | ValueTransformer[];
/** Array type indicator (PostgreSQL) */
array?: boolean;
/** Enum values */
enum?: Object | (string | number)[];
/** Spatial type indicator */
spatialFeatureType?: string;
/** SRID for spatial types */
srid?: number;
/** Creation date column */
createDate?: boolean;
/** Update date column */
updateDate?: boolean;
/** Delete date column (soft delete) */
deleteDate?: boolean;
/** Version column */
version?: boolean;
/** Tree level column */
treeLevel?: boolean;
/** Tree parent column */
treeParent?: boolean;
/** Tree children relation */
treeChildren?: boolean;
}
/**
* Value transformer interface for column serialization
*/
interface ValueTransformer {
/**
* Transforms value from entity to database
* @param value - Entity value
* @returns Database value
*/
to(value: any): any;
/**
* Transforms value from database to entity
* @param value - Database value
* @returns Entity value
*/
from(value: any): any;
}Configuration for relationships in entity schemas.
/**
* Relation definition options for entity schema
*/
interface EntitySchemaRelationOptions {
/** Relation type */
type: "one-to-one" | "one-to-many" | "many-to-one" | "many-to-many";
/** Target entity */
target: string | Function;
/** Inverse side property */
inverseSide?: string;
/** Whether relation is eager loaded */
eager?: boolean;
/** Cascade options */
cascade?: boolean | ("insert" | "update" | "remove" | "soft-remove" | "recover")[];
/** Whether relation is nullable */
nullable?: boolean;
/** Foreign key constraint name */
foreignKeyConstraintName?: string;
/** Create foreign key constraints */
createForeignKeyConstraints?: boolean;
/** Join column options */
joinColumn?: EntitySchemaJoinColumnOptions | EntitySchemaJoinColumnOptions[];
/** Join table options */
joinTable?: EntitySchemaJoinTableOptions;
/** Default ordering for relation */
orderBy?: Record<string, "ASC" | "DESC">;
/** Persistence enabled */
persistence?: boolean;
/** Primary relation indicator */
primary?: boolean;
}
/**
* Join column configuration for relations
*/
interface EntitySchemaJoinColumnOptions {
/** Join column name */
name?: string;
/** Referenced column name */
referencedColumnName?: string;
/** Foreign key constraint name */
foreignKeyConstraintName?: string;
}
/**
* Join table configuration for many-to-many relations
*/
interface EntitySchemaJoinTableOptions {
/** Join table name */
name?: string;
/** Database name */
database?: string;
/** Schema name */
schema?: string;
/** Join column (owner side) */
joinColumn?: EntitySchemaJoinColumnOptions;
/** Inverse join column */
inverseJoinColumn?: EntitySchemaJoinColumnOptions;
}Schema definitions for indexes and constraints.
/**
* Index definition options for entity schema
*/
interface EntitySchemaIndexOptions {
/** Index name */
name?: string;
/** Indexed column names */
columns: string[];
/** Whether index is unique */
unique?: boolean;
/** Whether index is spatial (MySQL) */
spatial?: boolean;
/** Whether index is fulltext (MySQL) */
fulltext?: boolean;
/** Index method (PostgreSQL) */
using?: string;
/** Partial index condition (PostgreSQL) */
where?: string;
/** Whether to synchronize */
synchronize?: boolean;
}
/**
* Unique constraint definition options
*/
interface EntitySchemaUniqueOptions {
/** Constraint name */
name?: string;
/** Column names for unique constraint */
columns: string[];
}
/**
* Check constraint definition options
*/
interface EntitySchemaCheckOptions {
/** Constraint name */
name: string;
/** Check expression */
expression: string;
}
/**
* Exclusion constraint definition options (PostgreSQL)
*/
interface EntitySchemaExclusionOptions {
/** Constraint name */
name?: string;
/** Exclusion expression */
expression: string;
}Entity Schema Examples:
import { EntitySchema } from "typeorm";
// Define User entity without decorators
interface User {
id: number;
name: string;
email: string;
createdAt: Date;
updatedAt: Date;
posts: Post[];
profile: Profile;
}
export const UserSchema = new EntitySchema<User>({
name: "User",
tableName: "users",
columns: {
id: {
type: "int",
primary: true,
generated: "increment"
},
name: {
type: "varchar",
length: 100,
nullable: false
},
email: {
type: "varchar",
length: 255,
unique: true,
nullable: false
},
createdAt: {
type: "timestamp",
createDate: true
},
updatedAt: {
type: "timestamp",
updateDate: true
}
},
relations: {
posts: {
type: "one-to-many",
target: "Post",
inverseSide: "author",
cascade: ["insert", "update"]
},
profile: {
type: "one-to-one",
target: "Profile",
joinColumn: {
name: "profile_id",
referencedColumnName: "id"
},
cascade: true,
eager: true
}
},
indices: [
{
name: "IDX_USER_EMAIL",
columns: ["email"],
unique: true
},
{
name: "IDX_USER_NAME",
columns: ["name"]
}
],
uniques: [
{
name: "UQ_USER_EMAIL",
columns: ["email"]
}
],
checks: [
{
name: "CHK_USER_NAME_NOT_EMPTY",
expression: "LENGTH(name) > 0"
}
]
});
// Define Post entity
interface Post {
id: number;
title: string;
content: string;
publishedAt: Date | null;
author: User;
tags: Tag[];
}
export const PostSchema = new EntitySchema<Post>({
name: "Post",
tableName: "posts",
columns: {
id: {
type: "int",
primary: true,
generated: "increment"
},
title: {
type: "varchar",
length: 255,
nullable: false
},
content: {
type: "text",
nullable: false
},
publishedAt: {
type: "timestamp",
nullable: true
}
},
relations: {
author: {
type: "many-to-one",
target: "User",
inverseSide: "posts",
joinColumn: {
name: "author_id"
},
nullable: false
},
tags: {
type: "many-to-many",
target: "Tag",
joinTable: {
name: "post_tags",
joinColumn: { name: "post_id" },
inverseJoinColumn: { name: "tag_id" }
},
cascade: true
}
},
orderBy: {
publishedAt: "DESC"
}
});Create entity schemas dynamically at runtime:
function createDynamicEntitySchema(
name: string,
fields: Array<{
name: string;
type: string;
required: boolean;
unique?: boolean;
}>
): EntitySchema {
const columns: any = {};
// Always include ID field
columns.id = {
type: "int",
primary: true,
generated: "increment"
};
// Add dynamic fields
fields.forEach(field => {
columns[field.name] = {
type: field.type,
nullable: !field.required,
unique: field.unique || false
};
});
// Add audit fields
columns.createdAt = {
type: "timestamp",
createDate: true
};
columns.updatedAt = {
type: "timestamp",
updateDate: true
};
return new EntitySchema({
name,
tableName: name.toLowerCase() + 's',
columns
});
}
// Usage
const ProductSchema = createDynamicEntitySchema('Product', [
{ name: 'name', type: 'varchar', required: true },
{ name: 'description', type: 'text', required: false },
{ name: 'price', type: 'decimal', required: true },
{ name: 'sku', type: 'varchar', required: true, unique: true }
]);Use entity schemas alongside decorator-based entities:
// Traditional decorator approach
@Entity()
export class Category {
@PrimaryGeneratedColumn()
id: number;
@Column()
name: string;
@OneToMany(() => Product, product => product.category)
products: Product[];
}
// Schema-based approach
interface Product {
id: number;
name: string;
price: number;
category: Category;
}
export const ProductSchema = new EntitySchema<Product>({
name: "Product",
columns: {
id: { type: "int", primary: true, generated: "increment" },
name: { type: "varchar", length: 255 },
price: { type: "decimal", precision: 10, scale: 2 }
},
relations: {
category: {
type: "many-to-one",
target: Category, // Reference decorator-based entity
inverseSide: "products"
}
}
});
// Use both in DataSource
const dataSource = new DataSource({
// ...
entities: [Category, ProductSchema]
});Entity schemas are ideal for:
Convert decorator-based entities to schemas:
// Before: Decorator-based
@Entity("users")
export class User {
@PrimaryGeneratedColumn()
id: number;
@Column({ unique: true })
email: string;
@OneToMany(() => Post, post => post.author)
posts: Post[];
}
// After: Schema-based
export const UserSchema = new EntitySchema<User>({
name: "User",
tableName: "users",
columns: {
id: { type: "int", primary: true, generated: "increment" },
email: { type: "varchar", unique: true }
},
relations: {
posts: {
type: "one-to-many",
target: "Post",
inverseSide: "author"
}
}
});Install with Tessl CLI
npx tessl i tessl/npm-typeorm