@andrewcaires/sequelize
v3.2.1
Published
Decorators for sequelize
Readme
sequelize
TypeScript decorators for Sequelize v6. Declare models, columns, associations and hooks right on the class — no manual Model.init() or Model.hasMany/belongsTo.
Also includes:
- Multi-connection support with schema replication.
- Safe
where/include/findparsers for API payloads. - Lightweight column sync via
updateAttributes().
Installation
npm i @andrewcaires/sequelizeRequires TypeScript with experimentalDecorators and emitDecoratorMetadata enabled.
Links
Quick start
import { Column, Database, DefaultValue, Model, ModelName, Optional, Sequelize } from "@andrewcaires/sequelize";
export enum UserStatus {
ACTIVE = "active",
DISABLED = "disabled",
}
const sequelize = new Sequelize({
uri: "mariadb://root:[email protected]:3306/sequelize",
});
@Database(sequelize)
@ModelName("user")
class UserModel extends Model<UserModel> {
@Column.Id(7)
declare readonly id: Optional<string>;
@Column.String()
declare name: Optional<string>;
@Column.Enum(UserStatus)
@DefaultValue(UserStatus.ACTIVE)
declare status: UserStatus;
@Column.CreatedAt()
declare readonly created_at: Optional<Date>;
@Column.UpdatedAt()
declare readonly updated_at: Optional<Date>;
@Column.DeletedAt()
declare readonly deleted_at: Optional<Date | null>;
}
await sequelize.sync();
const user = await UserModel.create({ name: "John", status: UserStatus.ACTIVE });Sequelize
Extends the native Sequelize and accepts all of its options, plus:
| option | type | description |
|-------------|--------------------|---------------------------------------------------------------------|
| id | string \| symbol | Instance id. Retrieve it later with Sequelize.getInstance(id). |
| uri | string | Connection URI. Defaults to sqlite::memory:. |
| prefix | string | Prefix prepended to modelName / tableName on sync(). |
| timeout | string | Connection timeout (e.g. "10s", "2m"). Defaults to 10s. |
| replicate | Sequelize | Another instance whose models should be cloned onto this one. |
Applied defaults: logging: false, define.timestamps: false, timezone: "+00:00".
Methods
sync(options?)— initializes every@Databasemodel, resolves associations and calls the originalsync.addModel(model)— manually registers a model.getModels()/getId()— introspection.static getInstance(id)/getInstances()— access registered instances.replicate(other)— clones the models fromotheronto this instance.
Model
Extends Sequelize's Model with a few helpers:
static getInstance<M>(id)— returns theModelStaticbound to connectionid.static version()— reads the@Versionmetadata.static updateAttributes()— diffs the current columns against the DB usingQueryInterface(changeColumn/addColumn). Handy as a lightweight migration in development.
Class decorators
| decorator | description |
|------------------------|--------------------------------------------------------------------|
| @Database(seq) | Binds the model to a Sequelize instance (required). |
| @ModelName(name) | Sets modelName (table name is pluralized). |
| @TableName(name) | Sets tableName as-is. |
| @Name(name, plural?) | Shortcut: true → ModelName, false → TableName. |
| @Underscored(flag?) | Enables underscored. |
| @Indexes([...]) | Declares ModelIndexesOptions[]. |
| @Scopes({...}) | Named scopes. |
| @DefaultScope({...}) | Default scope. |
| @Hooks({...}) | Registers multiple model hooks at once. |
| @Version("1.0.0") | Tags the model version (exposed via Model.version()). |
Column decorators
All types live under the Column namespace (Column.<Type>()).
Data types
BigInt, Blob, Boolean, Char, DateOnly, Decimal, Double, Enum, Float, FullDate, Integer, Json, String(length?, binary?), Text, Time, Timestamp, UUIDV1, UUIDV4, UUIDV7.
Primary key
Column.Id()— auto-incrementINTEGER.Column.Id(1 | 4 | 7)—UUIDwithUUIDV1/UUIDV4/UUIDV7default.
Timestamps
Column.CreatedAt()/Column.UpdatedAt()— maps the property tocreatedAt/updatedAt.Column.DeletedAt()— maps todeletedAtand enablesparanoid: trueautomatically.
Modifiers
| decorator | effect |
|--------------------|--------------------------------------|
| @PrimaryKey | Marks the column as PK. |
| @AutoIncrement | Marks as auto-increment. |
| @AllowNull(bool) | Sets allowNull. |
| @NotNull | Shortcut for allowNull: false. |
| @Unique(bool) | Sets uniqueness. |
| @DefaultValue(v) | Default value (literal or function). |
| @Comment(str) | SQL comment. |
| @Index2(opts?) | Column-level index. |
Association decorators
Always declared as @Association.<Kind>(() => OtherModel, opts?) — the arrow function avoids circular imports.
| decorator | maps to |
|----------------------------------------------------|-----------------------------------------|
| @Association.BelongsTo(() => M) | model.belongsTo(M) |
| @Association.HasOne(() => M) | model.hasOne(M) |
| @Association.HasMany(() => M) | model.hasMany(M) |
| @Association.BelongsToMany(() => M, () => Thru) | model.belongsToMany(M, { through }) |
| @Association.OneToOne(() => M) | hasOne + belongsTo |
| @Association.OneToMany(() => M) | hasMany + belongsTo |
Defaults: foreignKey ← decorated property name, onDelete: "CASCADE", as ← target tableName (for BelongsTo). Through-table columns are stripped from query results automatically via a global beforeFind hook.
Method decorators (hooks)
@Database(sequelize)
@ModelName("user")
class UserModel extends Model<UserModel> {
@Column.Id(7)
declare readonly id: Optional<string>;
@BeforeCreate()
static hashPassword(user: UserModel) {
// ...
}
}Use @Hook("beforeCreate") for custom hooks, or any of the shortcuts in decorators/method/hooks.
Multiple connections
const primary = new Sequelize({ id: "primary", uri: "mariadb://..." });
const replica = new Sequelize({ id: "replica", uri: "mariadb://...", replicate: primary });
await primary.sync();
await replica.sync();
const UserPrimary = UserModel.getInstance("primary");
const UserReplica = UserModel.getInstance("replica");API-safe parsers
Helpers to validate filters coming from HTTP requests without exposing the full Sequelize surface.
Where
Converts JSON with friendly operators into Sequelize Op symbols, validated against an attribute allowlist.
import { Where } from "@andrewcaires/sequelize";
const { value, error } = Where.parse(
["name", "active"],
JSON.stringify({ "$AND": [{ name: { "$LIKE": "%john%" } }, { active: true }] }),
);
// value → { [Op.and]: [{ name: { [Op.like]: "%john%" } }, { active: true }] }Supported aliases: $OR/||, $AND/&&, $EQ/=/==, $NE/!=, $GT/>, $GTE/>=, $LT/<, $LTE/<=, $IS, $NOT, $BETWEEN, $NOTBETWEEN, $REGEXP, $NOTREGEXP, $IN, $NOTIN, $LIKE, $NOTLIKE, $STARTSWITH, $ENDSWITH, $SUBSTRING.
Find and Include
Find.parse(schema, text) validates a payload containing select, where, having, include, order, group, limit and offset, restricted to the attributes/associations declared in FindSchema / IncludeSchema. Designed for endpoints like GET /resource?q=....
