Define an Entity
Describe a table as a decorated class, with explicit column types, tracked changes, and no surprises in the SQL.
An entity is a class whose decorators describe a table. Base derives migrations from it, repositories are typed by it, and change tracking rides on it.
The shape of an entity
Three rules, all lint-enforced or migration-relevant:
-
Extend
OrmTrackingEntity: it provides change tracking (updates send only changed fields), the staticfrom(...)constructor, and the accessor machinery behind rule 2. -
declareon every decorated property. The base class installs tracking accessors per column on the class's prototype (once, on first construction);declarekeeps the property type-only so those accessors survive. Thebase/orm-column-requires-declarelint rule catches omissions.One consequence worth knowing: because columns are prototype accessors,
{ ...entity },Object.keys(entity), andObject.assign({}, entity)do not see them; they see only internal tracking state. To get a plain object, useentity.toJSON()(whatJSON.stringifyandResponse.jsonalready call). -
Column types are explicit. Every
@OrmColumnstates its database type, and nothing is inferred from the TypeScript type, so the generated SQL never surprises you.
Column types
In @OrmColumn(type, options?), the first argument is a type descriptor:
| Kind | Descriptor | Notes |
|---|---|---|
| String | { kind: 'varchar', length: 255 }, { kind: 'char', length: 2 }, { kind: 'text' } | varchar/char require a length |
| Numbers | { kind: 'integer', size: 'int32' }, { kind: 'float' }, { kind: 'double' } | integer supports unsigned, increment |
| 64-bit | { kind: 'bigint', mode: 'number' | 'bigint' } | mode is required; you must decide how 64-bit values surface in JS |
| Exact decimal | { kind: 'decimal', precision, scale, mode: ... } | for money-like values |
| Other | { kind: 'boolean' }, { kind: 'datetime' }, { kind: 'uuid' }, { kind: 'json' }, { kind: 'bytes', size }, { kind: 'enum', values: [...] } |
The second argument holds column options like { nullable: true }; pair it with | null in the TypeScript type.
Primary keys and date columns
@OrmPrimaryAutoColumn('uuid'): a primary key that generates its value for you.@OrmPrimaryKey(...): for natural or composite keys (see the reference).@OrmCreateDateColumn()/@OrmUpdateDateColumn(): set on insert / maintained on update automatically.
Indexes and uniqueness have their own decorators: @OrmColumnIndex, @OrmColumnUnique, @OrmColumnUniqueIndex on columns, and @OrmTableIndex, @OrmTableUnique, @OrmTableUniqueIndex on the class.
On MySQL, a long text column can't be indexed whole: InnoDB caps an index key at 3072 bytes, and utf8mb4 reserves 4 bytes per declared character, so a varchar(1024) alone is over the cap. Declare a prefix index instead, an index over the first N characters:
Schema generation checks the math for you: an index MySQL would reject with errno 1071 fails at generate time with the offending index named, instead of half-applying a migration. SQLite has no key-length ceiling, so it ignores prefixLength and indexes the whole column.
Register the entity
An entity exists once it's listed in a database's entities in settings.ts:
Then generate a migration for it; see Migrations.