CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-typeorm

Data-Mapper ORM for TypeScript and ES2021+ supporting MySQL/MariaDB, PostgreSQL, MS SQL Server, Oracle, SAP HANA, SQLite, MongoDB databases.

Overview
Eval results
Files

entity-schema.mddocs/

Entity Schema (Code-First)

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.

Capabilities

EntitySchema Class

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;
}

Column Schema Options

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;
}

Relation Schema Options

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;
}

Index and Constraint Options

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"
  }
});

Dynamic Schema Generation

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 }
]);

Mixed Entity Definitions

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]
});

Use Cases

Entity schemas are ideal for:

  • Dynamic applications where entity structure is determined at runtime
  • Multi-tenant applications with varying schemas per tenant
  • Configuration-driven systems where entities are defined in external configuration
  • Legacy integration where decorator modification is not possible
  • Code generation where entities are generated from external sources

Migration from Decorators

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

docs

data-source.md

entity-definition.md

entity-schema.md

events.md

find-options.md

index.md

migrations.md

query-builder.md

relationships.md

repository.md

tile.json