@andrewcaires/sequelize
v3.3.0
Published
Decorators for sequelize
Readme
@andrewcaires/sequelize
TypeScript decorators and utilities for defining Sequelize v6 models directly on classes.
Use this package to declare columns, associations, hooks, scopes, and indexes
without calling Model.init() or the native association methods manually. It
also supports multiple database connections, model replication, lightweight
column synchronization, and allowlisted where parsing.
Installation
npm install @andrewcaires/sequelizeInstall the driver for the dialect used by your application. For MariaDB:
npm install mariadbEnable legacy decorators and decorator metadata in tsconfig.json:
{
"compilerOptions": {
"emitDecoratorMetadata": true,
"experimentalDecorators": true
}
}The package exposes a single public entrypoint, @andrewcaires/sequelize, with
ESM, CommonJS, and TypeScript declaration targets.
Usage
import {
Column,
Database,
DefaultValue,
Model,
ModelName,
Optional,
Sequelize,
} from "@andrewcaires/sequelize";
enum UserStatus {
Active = "active",
Disabled = "disabled",
}
const sequelize = new Sequelize({
uri: "mariadb://root:[email protected]:3306/example",
});
@Database(sequelize)
@ModelName("user")
class UserModel extends Model<UserModel> {
@Column.Id(7)
declare readonly id: Optional<string>;
@Column.String()
declare name: string;
@Column.Enum(UserStatus)
@DefaultValue(UserStatus.Active)
declare status: UserStatus;
@Column.CreatedAt()
declare readonly createdAt: Optional<Date>;
@Column.UpdatedAt()
declare readonly updatedAt: Optional<Date>;
}
await sequelize.sync();
const user = await UserModel.create({
name: "John",
status: UserStatus.Active,
});sequelize.sync() initializes every model registered with @Database, applies
its metadata, resolves associations, and then calls Sequelize's native sync.
API
All public classes, decorators, helpers, and types are exported from
@andrewcaires/sequelize.
Sequelize
Extends the native Sequelize class with model registration, instance lookup, and replication between connections.
Options
The constructor accepts Sequelize's native options and these additions:
| Option | Type | Description |
| --- | --- | --- |
| id | TypeKey | Identifier used to retrieve the connection and its bound models. |
| uri | string | Connection URI. |
| prefix | string | Prefix added to model and table names during initialization. |
| timeout | string | Connection timeout such as "10s" or "2m". Defaults to "10s". |
| replicate | Sequelize | Connection whose registered models should be cloned onto this instance. |
Unless overridden, the constructor disables logging and timestamps and uses the
+00:00 timezone. Passing logging: true enables the package's SQL logger.
Methods
declare class Sequelize extends NativeSequelize {
addModel(model: typeof Model): void;
addModels(...models: Array<typeof Model>): void;
getId(): TypeKey;
getModels(): Array<typeof Model>;
replicate(sequelize: Sequelize): void;
sync(options?: SyncOptions): Promise<this>;
static getInstance(id: TypeKey): Sequelize;
static getInstances(): Array<Sequelize>;
}Model
Extends Sequelize's native Model and provides helpers for connections and
decorator metadata.
Methods
declare class Model<T extends Model = any, K extends Model = T>
extends NativeModel<InferAttributes<T>, InferCreationAttributes<K>> {
static getInstance<M extends Model>(id: TypeKey): ModelStatic<M>;
static updateAttributes(): Promise<void>;
static version(): string;
}getInstance(id)returns the model bound to a specific connection.version()returns the value declared with@Version, or"0.0.0".updateAttributes()tries to change each non-primary-key column and adds it when it does not exist. This is a lightweight development helper, not a replacement for production migrations.
Class decorators
Class decorators configure the model metadata consumed by Sequelize.sync().
| Decorator | Description |
| --- | --- |
| @Database(connection) | Registers the model with a Sequelize instance or connection id. |
| @ModelName(name) | Sets the model name and lets Sequelize derive the table name. |
| @TableName(name) | Sets the table name directly. |
| @Name(name, pluralized?) | Uses ModelName when pluralized is true, otherwise TableName. |
| @Underscored(value) | Controls Sequelize's underscored model option. |
| @Indexes(...indexes) | Declares one or more model indexes. |
| @Scopes(scopes) | Declares named scopes. |
| @DefaultScope(scope) | Declares the default scope. |
| @Hooks(hooks) | Registers multiple Sequelize hooks. |
| @Version(value) | Stores an application-defined model version. |
Column decorators
Column types are available through the Column namespace.
import { Column } from "@andrewcaires/sequelize";
@Column.String(120)
declare title: string;
@Column.Decimal(10, 2)
declare price: number;
@Column.Json()
declare metadata: Record<string, unknown>;Data types
- Numeric:
BigInt,Decimal,Double,Float, andInteger. - Text and binary:
Blob,Char,String, andText. - Date and time:
DateOnly,FullDate,Time,Timestamp. - Other values:
Boolean,Enum,Json,UUIDV1,UUIDV4, andUUIDV7.
Use @Column.Id() for an auto-incrementing integer primary key, or pass 1,
4, or 7 to create a UUID primary key with the matching default generator.
Timestamps
| Decorator | Description |
| --- | --- |
| @Column.CreatedAt() | Maps the property to Sequelize's creation timestamp. |
| @Column.UpdatedAt() | Maps the property to Sequelize's update timestamp. |
| @Column.DeletedAt() | Maps the property to the deletion timestamp and enables paranoid mode. |
Modifiers
| Decorator | Description |
| --- | --- |
| @AllowNull() | Sets allowNull: true. |
| @NotNull() | Sets allowNull: false. |
| @PrimaryKey() | Marks the column as a primary key. |
| @AutoIncrement() | Enables auto-increment. |
| @Unique(value) | Controls the column's unique constraint. |
| @DefaultValue(value) | Sets a literal or function default value. |
| @Comment(value) | Adds a SQL column comment. |
| @Index(options?) | Adds the column to a model index. |
| @Value(callback, force?) | Computes the property before create and update operations. |
Association decorators
Associations receive callback functions so related models can reference one another without eager evaluation.
import { Association } from "@andrewcaires/sequelize";
@Association.BelongsTo(() => CompanyModel)
declare companyId: string;
@Association.HasMany(() => PostModel)
declare posts?: Array<PostModel>;| Decorator | Native association |
| --- | --- |
| @Association.BelongsTo(() => Model, options?) | belongsTo |
| @Association.HasOne(() => Model, options?) | hasOne |
| @Association.HasMany(() => Model, options?) | hasMany |
| @Association.BelongsToMany(() => Model, () => ThroughModel, options?) | belongsToMany |
| @Association.OneToOne(() => Model, options?) | hasOne and belongsTo |
| @Association.OneToMany(() => Model, options?) | hasMany and belongsTo |
The decorators derive aliases and foreign keys from the decorated property and
model metadata when those options are omitted. Associations default to
onDelete: "CASCADE".
Method decorators
Use @Hook(type) to register any Sequelize model hook, or import a named
decorator such as @BeforeCreate(), @AfterUpdate(), or @BeforeFind().
import {
BeforeCreate,
Column,
Database,
Model,
ModelName,
Optional,
} from "@andrewcaires/sequelize";
@Database(sequelize)
@ModelName("user")
class UserModel extends Model<UserModel> {
@Column.Id(7)
declare readonly id: Optional<string>;
@Column.String()
declare name: string;
@BeforeCreate()
static normalize(user: UserModel): void {
user.name = user.name.trim();
}
}Named decorators are provided for the supported before and after create,
update, save, destroy, restore, find, query, sync, upsert, validate, count, and
bulk operation hooks.
Multiple connections
Give each connection an id to retrieve the correct model class later.
const primary = new Sequelize({
id: "primary",
uri: "mariadb://root:[email protected]:3306/primary",
});
const replica = new Sequelize({
id: "replica",
replicate: primary,
uri: "mariadb://root:[email protected]:3306/replica",
});
await primary.sync();
await replica.sync();
const PrimaryUser = UserModel.getInstance<UserModel>("primary");
const ReplicaUser = UserModel.getInstance<UserModel>("replica");Where
Where.parse recursively converts allowlisted operator aliases into Sequelize
Op symbols and rejects attributes outside the supplied allowlist.
Signature
declare class Where {
static parse(attributes: Array<string>, object: TypeAnyObject): TypeAnyObject;
static value(
attributes: Array<string>,
value: unknown,
): TypeScalar | TypeAnyObject | Array<TypeAnyObject>;
}Example
import { Op, Where } from "@andrewcaires/sequelize";
const where = Where.parse(["name", "active"], {
$AND: [
{ name: { $LIKE: "%john%" } },
{ active: true },
],
});
// {
// [Op.and]: [
// { name: { [Op.like]: "%john%" } },
// { active: true },
// ],
// }Supported aliases include $OR, $AND, $EQ, $NE, $GT, $GTE, $LT,
$LTE, $IS, $NOT, $BETWEEN, $NOTBETWEEN, $REGEXP, $NOTREGEXP,
$IN, $NOTIN, $LIKE, $NOTLIKE, $STARTSWITH, $ENDSWITH, and
$SUBSTRING. Symbolic alternatives such as ||, &&, =, !=, >, >=,
<, and <= are also supported.
Type exports
The package re-exports Sequelize's DataType, DataTypes, and Op, together
with types used by its decorators and model helpers.
import type {
ModelCreateOptions,
ModelFindOptions,
ModelOptions,
ModelUpdateOptions,
OneToManyOptions,
OneToOneOptions,
Optional,
Options,
} from "@andrewcaires/sequelize";Development
npm run build
npm run check
npm run lint
npm test
npm run typecheckbuild runs ESLint and creates ESM, CommonJS, and declaration bundles with
Rollup. check runs the TypeScript compiler without emitting files and then
runs the test suite.
Links
- Package: npm
- Repository: andrewcaires/npm
- Issues: GitHub Issues
- Sequelize: official documentation
License
MIT
