0
# Entity Schema (Code-First)
1
2
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.
3
4
## Capabilities
5
6
### EntitySchema Class
7
8
Core class for defining entities programmatically without using decorator syntax.
9
10
```typescript { .api }
11
/**
12
* Programmatic entity definition without decorators
13
* @template T - Entity type
14
*/
15
class EntitySchema<T = any> {
16
/**
17
* Creates new entity schema
18
* @param options - Schema definition options
19
*/
20
constructor(options: EntitySchemaOptions<T>);
21
22
/** Schema configuration options */
23
readonly options: EntitySchemaOptions<T>;
24
}
25
26
/**
27
* Configuration options for entity schema
28
*/
29
interface EntitySchemaOptions<T> {
30
/** Entity name */
31
name: string;
32
33
/** Target constructor function (optional) */
34
target?: Function;
35
36
/** Database table name */
37
tableName?: string;
38
39
/** Database name */
40
database?: string;
41
42
/** Schema name */
43
schema?: string;
44
45
/** Column definitions */
46
columns: { [P in keyof T]: EntitySchemaColumnOptions };
47
48
/** Relation definitions */
49
relations?: { [key: string]: EntitySchemaRelationOptions };
50
51
/** Index definitions */
52
indices?: EntitySchemaIndexOptions[];
53
54
/** Unique constraint definitions */
55
uniques?: EntitySchemaUniqueOptions[];
56
57
/** Check constraint definitions */
58
checks?: EntitySchemaCheckOptions[];
59
60
/** Exclusion constraint definitions */
61
exclusions?: EntitySchemaExclusionOptions[];
62
63
/** Table inheritance configuration */
64
inheritancePattern?: "STI" | "CTI";
65
66
/** Discriminator column (for STI) */
67
discriminatorColumn?: string;
68
69
/** Default ordering */
70
orderBy?: Record<string, "ASC" | "DESC">;
71
72
/** Table engine (MySQL) */
73
engine?: string;
74
75
/** Synchronization flag */
76
synchronize?: boolean;
77
}
78
```
79
80
### Column Schema Options
81
82
Detailed column configuration for entity schemas.
83
84
```typescript { .api }
85
/**
86
* Column definition options for entity schema
87
*/
88
interface EntitySchemaColumnOptions {
89
/** Column database type */
90
type: ColumnType;
91
92
/** Column name in database */
93
name?: string;
94
95
/** Column length (for string types) */
96
length?: string | number;
97
98
/** Column precision (for decimal types) */
99
precision?: number;
100
101
/** Column scale (for decimal types) */
102
scale?: number;
103
104
/** Whether column allows NULL */
105
nullable?: boolean;
106
107
/** Default column value */
108
default?: any;
109
110
/** Whether column is primary key */
111
primary?: boolean;
112
113
/** Whether column is unique */
114
unique?: boolean;
115
116
/** Column comment */
117
comment?: string;
118
119
/** Whether column is generated */
120
generated?: true | "increment" | "uuid" | "rowid";
121
122
/** Whether column is select by default */
123
select?: boolean;
124
125
/** Whether column participates in inserts */
126
insert?: boolean;
127
128
/** Whether column participates in updates */
129
update?: boolean;
130
131
/** Column collation */
132
collation?: string;
133
134
/** Column charset */
135
charset?: string;
136
137
/** Value transformer */
138
transformer?: ValueTransformer | ValueTransformer[];
139
140
/** Array type indicator (PostgreSQL) */
141
array?: boolean;
142
143
/** Enum values */
144
enum?: Object | (string | number)[];
145
146
/** Spatial type indicator */
147
spatialFeatureType?: string;
148
149
/** SRID for spatial types */
150
srid?: number;
151
152
/** Creation date column */
153
createDate?: boolean;
154
155
/** Update date column */
156
updateDate?: boolean;
157
158
/** Delete date column (soft delete) */
159
deleteDate?: boolean;
160
161
/** Version column */
162
version?: boolean;
163
164
/** Tree level column */
165
treeLevel?: boolean;
166
167
/** Tree parent column */
168
treeParent?: boolean;
169
170
/** Tree children relation */
171
treeChildren?: boolean;
172
}
173
174
/**
175
* Value transformer interface for column serialization
176
*/
177
interface ValueTransformer {
178
/**
179
* Transforms value from entity to database
180
* @param value - Entity value
181
* @returns Database value
182
*/
183
to(value: any): any;
184
185
/**
186
* Transforms value from database to entity
187
* @param value - Database value
188
* @returns Entity value
189
*/
190
from(value: any): any;
191
}
192
```
193
194
### Relation Schema Options
195
196
Configuration for relationships in entity schemas.
197
198
```typescript { .api }
199
/**
200
* Relation definition options for entity schema
201
*/
202
interface EntitySchemaRelationOptions {
203
/** Relation type */
204
type: "one-to-one" | "one-to-many" | "many-to-one" | "many-to-many";
205
206
/** Target entity */
207
target: string | Function;
208
209
/** Inverse side property */
210
inverseSide?: string;
211
212
/** Whether relation is eager loaded */
213
eager?: boolean;
214
215
/** Cascade options */
216
cascade?: boolean | ("insert" | "update" | "remove" | "soft-remove" | "recover")[];
217
218
/** Whether relation is nullable */
219
nullable?: boolean;
220
221
/** Foreign key constraint name */
222
foreignKeyConstraintName?: string;
223
224
/** Create foreign key constraints */
225
createForeignKeyConstraints?: boolean;
226
227
/** Join column options */
228
joinColumn?: EntitySchemaJoinColumnOptions | EntitySchemaJoinColumnOptions[];
229
230
/** Join table options */
231
joinTable?: EntitySchemaJoinTableOptions;
232
233
/** Default ordering for relation */
234
orderBy?: Record<string, "ASC" | "DESC">;
235
236
/** Persistence enabled */
237
persistence?: boolean;
238
239
/** Primary relation indicator */
240
primary?: boolean;
241
}
242
243
/**
244
* Join column configuration for relations
245
*/
246
interface EntitySchemaJoinColumnOptions {
247
/** Join column name */
248
name?: string;
249
250
/** Referenced column name */
251
referencedColumnName?: string;
252
253
/** Foreign key constraint name */
254
foreignKeyConstraintName?: string;
255
}
256
257
/**
258
* Join table configuration for many-to-many relations
259
*/
260
interface EntitySchemaJoinTableOptions {
261
/** Join table name */
262
name?: string;
263
264
/** Database name */
265
database?: string;
266
267
/** Schema name */
268
schema?: string;
269
270
/** Join column (owner side) */
271
joinColumn?: EntitySchemaJoinColumnOptions;
272
273
/** Inverse join column */
274
inverseJoinColumn?: EntitySchemaJoinColumnOptions;
275
}
276
```
277
278
### Index and Constraint Options
279
280
Schema definitions for indexes and constraints.
281
282
```typescript { .api }
283
/**
284
* Index definition options for entity schema
285
*/
286
interface EntitySchemaIndexOptions {
287
/** Index name */
288
name?: string;
289
290
/** Indexed column names */
291
columns: string[];
292
293
/** Whether index is unique */
294
unique?: boolean;
295
296
/** Whether index is spatial (MySQL) */
297
spatial?: boolean;
298
299
/** Whether index is fulltext (MySQL) */
300
fulltext?: boolean;
301
302
/** Index method (PostgreSQL) */
303
using?: string;
304
305
/** Partial index condition (PostgreSQL) */
306
where?: string;
307
308
/** Whether to synchronize */
309
synchronize?: boolean;
310
}
311
312
/**
313
* Unique constraint definition options
314
*/
315
interface EntitySchemaUniqueOptions {
316
/** Constraint name */
317
name?: string;
318
319
/** Column names for unique constraint */
320
columns: string[];
321
}
322
323
/**
324
* Check constraint definition options
325
*/
326
interface EntitySchemaCheckOptions {
327
/** Constraint name */
328
name: string;
329
330
/** Check expression */
331
expression: string;
332
}
333
334
/**
335
* Exclusion constraint definition options (PostgreSQL)
336
*/
337
interface EntitySchemaExclusionOptions {
338
/** Constraint name */
339
name?: string;
340
341
/** Exclusion expression */
342
expression: string;
343
}
344
```
345
346
**Entity Schema Examples:**
347
348
```typescript
349
import { EntitySchema } from "typeorm";
350
351
// Define User entity without decorators
352
interface User {
353
id: number;
354
name: string;
355
email: string;
356
createdAt: Date;
357
updatedAt: Date;
358
posts: Post[];
359
profile: Profile;
360
}
361
362
export const UserSchema = new EntitySchema<User>({
363
name: "User",
364
tableName: "users",
365
columns: {
366
id: {
367
type: "int",
368
primary: true,
369
generated: "increment"
370
},
371
name: {
372
type: "varchar",
373
length: 100,
374
nullable: false
375
},
376
email: {
377
type: "varchar",
378
length: 255,
379
unique: true,
380
nullable: false
381
},
382
createdAt: {
383
type: "timestamp",
384
createDate: true
385
},
386
updatedAt: {
387
type: "timestamp",
388
updateDate: true
389
}
390
},
391
relations: {
392
posts: {
393
type: "one-to-many",
394
target: "Post",
395
inverseSide: "author",
396
cascade: ["insert", "update"]
397
},
398
profile: {
399
type: "one-to-one",
400
target: "Profile",
401
joinColumn: {
402
name: "profile_id",
403
referencedColumnName: "id"
404
},
405
cascade: true,
406
eager: true
407
}
408
},
409
indices: [
410
{
411
name: "IDX_USER_EMAIL",
412
columns: ["email"],
413
unique: true
414
},
415
{
416
name: "IDX_USER_NAME",
417
columns: ["name"]
418
}
419
],
420
uniques: [
421
{
422
name: "UQ_USER_EMAIL",
423
columns: ["email"]
424
}
425
],
426
checks: [
427
{
428
name: "CHK_USER_NAME_NOT_EMPTY",
429
expression: "LENGTH(name) > 0"
430
}
431
]
432
});
433
434
// Define Post entity
435
interface Post {
436
id: number;
437
title: string;
438
content: string;
439
publishedAt: Date | null;
440
author: User;
441
tags: Tag[];
442
}
443
444
export const PostSchema = new EntitySchema<Post>({
445
name: "Post",
446
tableName: "posts",
447
columns: {
448
id: {
449
type: "int",
450
primary: true,
451
generated: "increment"
452
},
453
title: {
454
type: "varchar",
455
length: 255,
456
nullable: false
457
},
458
content: {
459
type: "text",
460
nullable: false
461
},
462
publishedAt: {
463
type: "timestamp",
464
nullable: true
465
}
466
},
467
relations: {
468
author: {
469
type: "many-to-one",
470
target: "User",
471
inverseSide: "posts",
472
joinColumn: {
473
name: "author_id"
474
},
475
nullable: false
476
},
477
tags: {
478
type: "many-to-many",
479
target: "Tag",
480
joinTable: {
481
name: "post_tags",
482
joinColumn: { name: "post_id" },
483
inverseJoinColumn: { name: "tag_id" }
484
},
485
cascade: true
486
}
487
},
488
orderBy: {
489
publishedAt: "DESC"
490
}
491
});
492
```
493
494
### Dynamic Schema Generation
495
496
Create entity schemas dynamically at runtime:
497
498
```typescript
499
function createDynamicEntitySchema(
500
name: string,
501
fields: Array<{
502
name: string;
503
type: string;
504
required: boolean;
505
unique?: boolean;
506
}>
507
): EntitySchema {
508
const columns: any = {};
509
510
// Always include ID field
511
columns.id = {
512
type: "int",
513
primary: true,
514
generated: "increment"
515
};
516
517
// Add dynamic fields
518
fields.forEach(field => {
519
columns[field.name] = {
520
type: field.type,
521
nullable: !field.required,
522
unique: field.unique || false
523
};
524
});
525
526
// Add audit fields
527
columns.createdAt = {
528
type: "timestamp",
529
createDate: true
530
};
531
532
columns.updatedAt = {
533
type: "timestamp",
534
updateDate: true
535
};
536
537
return new EntitySchema({
538
name,
539
tableName: name.toLowerCase() + 's',
540
columns
541
});
542
}
543
544
// Usage
545
const ProductSchema = createDynamicEntitySchema('Product', [
546
{ name: 'name', type: 'varchar', required: true },
547
{ name: 'description', type: 'text', required: false },
548
{ name: 'price', type: 'decimal', required: true },
549
{ name: 'sku', type: 'varchar', required: true, unique: true }
550
]);
551
```
552
553
### Mixed Entity Definitions
554
555
Use entity schemas alongside decorator-based entities:
556
557
```typescript
558
// Traditional decorator approach
559
@Entity()
560
export class Category {
561
@PrimaryGeneratedColumn()
562
id: number;
563
564
@Column()
565
name: string;
566
567
@OneToMany(() => Product, product => product.category)
568
products: Product[];
569
}
570
571
// Schema-based approach
572
interface Product {
573
id: number;
574
name: string;
575
price: number;
576
category: Category;
577
}
578
579
export const ProductSchema = new EntitySchema<Product>({
580
name: "Product",
581
columns: {
582
id: { type: "int", primary: true, generated: "increment" },
583
name: { type: "varchar", length: 255 },
584
price: { type: "decimal", precision: 10, scale: 2 }
585
},
586
relations: {
587
category: {
588
type: "many-to-one",
589
target: Category, // Reference decorator-based entity
590
inverseSide: "products"
591
}
592
}
593
});
594
595
// Use both in DataSource
596
const dataSource = new DataSource({
597
// ...
598
entities: [Category, ProductSchema]
599
});
600
```
601
602
## Use Cases
603
604
Entity schemas are ideal for:
605
606
- **Dynamic applications** where entity structure is determined at runtime
607
- **Multi-tenant applications** with varying schemas per tenant
608
- **Configuration-driven systems** where entities are defined in external configuration
609
- **Legacy integration** where decorator modification is not possible
610
- **Code generation** where entities are generated from external sources
611
612
## Migration from Decorators
613
614
Convert decorator-based entities to schemas:
615
616
```typescript
617
// Before: Decorator-based
618
@Entity("users")
619
export class User {
620
@PrimaryGeneratedColumn()
621
id: number;
622
623
@Column({ unique: true })
624
email: string;
625
626
@OneToMany(() => Post, post => post.author)
627
posts: Post[];
628
}
629
630
// After: Schema-based
631
export const UserSchema = new EntitySchema<User>({
632
name: "User",
633
tableName: "users",
634
columns: {
635
id: { type: "int", primary: true, generated: "increment" },
636
email: { type: "varchar", unique: true }
637
},
638
relations: {
639
posts: {
640
type: "one-to-many",
641
target: "Post",
642
inverseSide: "author"
643
}
644
}
645
});
646
```