Decorators

Read this page in the documentation

Decorators Decorators are the class-based way to define a model: annotate properties instead of building an attributes object. They produce exactly the same model as prorm.define() — addModel() reads the metadata the decorators recorded and calls define() with it. Beyond the core model decorators there is a much larger set covering constraints, dialect-specific column types, schema objects, security and query hints. The complete per-decorator reference — every option, every file, and an honest note on which ones are stubs — is decorators. This guide covers how to use them and how they get wired. The core set Decorator | Applies to | Purpose | --- | --- | --- | @Table(options?) | class | tableName, schema, timestamps, underscored, paranoid, indexes, scopes, hooks — the same model options. | @Attribute(type) / @Column(type) | property | Declares the column and its data type. | @PrimaryKey | property | Primary key; implies allowNull: false. | @AutoIncrement | property | Auto-increment / SERIAL / IDENTITY. | @AllowNull(bool?) / @NotNull | property | Nullability. | @Default(value) | property | Static value, DataTypes.NOW, or a function. | @Unique(nameOrTrue?) | property | UNIQUE constraint. | @Comment(text) | property/class | Column or table comment. | Read the metadata back with getModelMetadata, getAttributeMetadata, getModelAttributes; clearMetadata() resets it, which tests want. Class inheritance works: @AbstractModel marks a base class whose attributes are inherited (getInheritedAttributeMetadata), so timestamps, paranoid and shared columns can be declared once. Associations Use @HasMany / @HasOne from src/decorators — they record real metadata: The () => Target thunk defers resolution, so two models can refer to each other regardless of import order. addModel() queues an association whose target is not registered yet and binds it once it is; sync() flushes whatever remains. @HasOne / @HasMany / @BelongsTo / @BelongsToMany are also exported from src/models/decorators.ts, where their bodies are empty stubs that exist only to provide typed signatures. The package root re-exports the functional versions explicitly, so a root import resolves to the working one. There is no @BelongsTo / @BelongsToMany implementation under src/decorators/ at all — declare those with Model.belongsTo(...) / Model.belongsToMany(...). See Associations. Decorators that need wiring Many feature decorators only record metadata; a companion call installs the hooks. Forgetting that call is the most common reason a decorator "does nothing". Decorator | Wire it with | --- | --- | @Secure / @SecureField | applySecureHooks(Model) — plus setEncryptionAdapter() | @Encrypt (compliance) | applyEncryptionHooks(Model) | @Mask (compliance) | applyMaskingHooks(Model) | @Timezone | applyTimezoneHooks(Model) | @Default (rich form) | applyDefaultHooks(Model) | @Procedure / @SqlFunction | prorm.registerProcedures(Class), then sync() | @View / @MaterializedView / @Trigger | prorm.addModel(Class), then sync() | @Security | setupSecurity(Model, prorm) or setupAllSecurity(prorm) | @ExternalField | prorm.registerStore(name, store) | The wider catalogue Grouped by what they do. Full options are in the decorators. Column behaviour — @Default (values, functions, sequences, SQL literals), @Comment, @Check / @CheckColumn / @CheckTable, @Generated / @Identity, @Secure / @SecureField (hash before save; bcrypt/argon2 adapters, library not bundled), @Timezone, @Collate / @SetCollate. Dialect column types — @UUID, @JSONB, @Range, @HStore (PostgreSQL); @JSONColumn, @SetColumn, @Spatial (MySQL/MariaDB). Constraints — @References, @OnDelete, @OnUpdate, @Deferrable, @Unique, @AutoIncrement, @Identity, @GeneratedAlways, @Exclusion. Schema objects — @View, @MaterializedView, @Trigger, @Procedure, @SqlFunction, @ForeignTable, @DatabaseSetting, @ForeignKeyChecks. Physical storage — @Storage, @Tablespace, @Clustered, @Compression, @ReplicaIdentity, @Statistics, @OnCommit, @Inherits, @ColumnStorage, @Partition. Query and session hints — @UseIndex / @ForceIndex / @IgnoreIndex, @Tablesample, @Distinct, @DistinctOn, @WithLock, @StatementTimeout, @IdleInTransactionTimeout, @Cursor, @Fetch, @FetchWith. Access & audit — @CanView / @CannotView / @CanWrite / @CannotWrite / @Permission (evaluated at query time in the ORM, not enforced by the database), @Audit (in-memory log — see Audit logging). Compliance — @Security, @Encrypt, @Mask, @Classify, @Pseudonymize, @RateLimit, @LineageTrack. See Compliance. External storage — @ExternalField. See External fields. Decorators do not check your dialect No decorator inspects the active dialect. Applying @JSONB, @HStore, @Range, @UUID, @Spatial or @ForeignTable to a model backed by SQLite will not throw — the metadata is recorded either way, and whether it produces correct SQL depends entirely on which code path consumes it later. They degrade silently rather than failing fast. So: keep PostgreSQL-flavoured decorators (@UUID, @JSONB, @HStore, @Range, @ForeignTable, @Tablespace, @Compression, @ReplicaIdentity, @Inherits, @Exclusion, @GeneratedAlways, @Tablesample, @DistinctOn) on PostgreSQL connections, and MySQL/MariaDB-flavoured ones (@JSONColumn, @SetColumn, @Spatial, @Storage engine options, the index hints, @DatabaseSetting, the collation constants) on MySQL/MariaDB. Metadata storage There is no single registry. src/models/decorators.ts keeps its own module-level maps; each file under src/decorators/ manages its own storage — some on the class, some in module maps, a few via reflect-metadata. Each family therefore exposes its own reader (getHasMany, getAllHasOne, getTriggers, getViewOptions, getMaskRules, getEncryptedFields, …). ORMDecorators (in orm-decorators.ts) is a plain object listing decorator names as strings, grouped into MODELDECORATORS, CONSTRAINTDECORATORS, ASSOCIATIONDECORATORS, HOOKDECORATORS, VALIDATIONDECORATORS, POSTGRESDECORATORS and SETTINGSDECORATORS. It is for tooling and reflection; it registers nothing. TypeScript setup Decorators need these compiler options: Declare properties with declare so TypeScript does not emit a class field that would shadow the model's accessor at runtime. Related reading decorators — the full reference Defining models — the define() equivalent Associations Compliance, External fields TypeScript types