Class-based Model (TypeScript)
import { Model, DataTypes, Sequelize, Optional } from "sequelize";
interface UserAttributes {
id: number;
email: string;
name: string | null;
role: "admin" | "user";
createdAt?: Date;
}
interface UserCreationAttributes extends Optional<UserAttributes, "id"> {}
class User extends Model<UserAttributes, UserCreationAttributes>
implements UserAttributes {
declare id: number;
declare email: string;
declare name: string | null;
declare role: "admin" | "user";
declare readonly createdAt: Date;
declare readonly updatedAt: Date;
static initialize(sequelize: Sequelize) {
User.init(
{
id: { type: DataTypes.INTEGER, autoIncrement: true, primaryKey: true },
email: { type: DataTypes.STRING, allowNull: false, unique: true,
validate: { isEmail: true } },
name: { type: DataTypes.STRING, allowNull: true },
role: { type: DataTypes.ENUM("admin","user"), defaultValue: "user" }
},
{
sequelize,
tableName: "users",
timestamps: true,
paranoid: true, // soft deletes (deletedAt column)
underscored: true // snake_case column names
}
);
}
}
Hooks
import * as bcrypt from "bcrypt";
User.addHook("beforeCreate", async (user) => {
if (user.password) {
user.password = await bcrypt.hash(user.password, 12);
}
});
User.addHook("afterCreate", async (user, options) => {
await AuditLog.create({ action: "user_created", userId: user.id });
});
// Inline hooks in init()
User.init({ ... }, {
sequelize,
hooks: {
beforeUpdate: async (user) => {
if (user.changed("email")) {
user.emailVerified = false;
}
}
}
});
// Available hooks:
// beforeValidate, afterValidate
// beforeCreate, afterCreate
// beforeUpdate, afterUpdate
// beforeSave, afterSave
// beforeDestroy, afterDestroy
// beforeBulkCreate, afterBulkCreate
Scopes
User.init({ ... }, {
sequelize,
defaultScope: {
where: { isActive: true },
attributes: { exclude: ["password"] }
},
scopes: {
withPassword: { attributes: { include: ["password"] } },
admins: { where: { role: "admin" } },
recent: { order: [["createdAt", "DESC"]], limit: 10 },
withPosts: {
include: [{ model: Post, as: "posts" }]
}
}
});
// Using scopes
User.scope("admins").findAll();
User.scope(["admins", "recent"]).findAll();
User.scope("withPassword").findOne({ where: { email: "alice@example.com" } });
User.unscoped().findAll(); // ignore defaultScope
Querying
const { Op } = require("sequelize");
// findAll with operators
await User.findAll({
where: {
age: { [Op.between]: [18, 65] },
email: { [Op.like]: "%@example.com" },
role: { [Op.in]: ["admin", "user"] },
[Op.or]: [
{ firstName: "Alice" },
{ lastName: "Smith" }
]
},
order: [["createdAt", "DESC"]],
limit: 20,
offset: 0,
attributes: ["id", "email", "name"]
});
// findOrCreate
const [user, created] = await User.findOrCreate({
where: { email: "alice@example.com" },
defaults: { name: "Alice", role: "user" }
});
// upsert
await User.upsert({ email: "alice@example.com", name: "Alice" });