ORM (Sequelize) — Basics
What is an ORM
Section titled “What is an ORM”Maps relational rows ↔ object instances. Generates SQL from method calls (User.findAll, user.save). Handles connection pooling, type coercion, lifecycle hooks, transactions.
Sequelize at a glance
Section titled “Sequelize at a glance”- Supports Postgres, MySQL/MariaDB, MSSQL, SQLite, Snowflake, DB2, Oracle.
- v6 = current stable; v7 alpha (full TS rewrite).
- Models defined via
Model.init(attrs, options)or@Tabledecorator (legacy decorators).
Core concepts
Section titled “Core concepts”- Model = JS class bound to a table.
- Attribute = column definition (type, allowNull, defaultValue, validate).
- Association =
belongsTo,hasOne,hasMany,belongsToMany. - Instance = single row in memory; has
save(),destroy(),reload(),changed(),previous(). - Finder =
findOne,findAll,findByPk,findOrCreate,findAndCountAll. - Scope = named pre-canned
where/includefragments.
Migration vs sync
Section titled “Migration vs sync”sync()reads model files, emitsCREATE TABLE IF NOT EXISTS. Not versioned, not reversible. Dev only.sequelize-climigrations = ordered files inmigrations/, tracked inSequelizeMeta. Use in CI/prod.sync({ alter: true })silently drops columns missing from models. Never run in prod.
Lifecycle hooks (quick reference)
Section titled “Lifecycle hooks (quick reference)”| Hook family | Fires |
|---|---|
beforeValidate / afterValidate | Before / after model validation |
beforeCreate / afterCreate | Single-row insert |
beforeUpdate / afterUpdate | Single-row update |
beforeDestroy / afterDestroy | Single-row delete |
beforeSave / afterSave | Both create + update |
beforeBulkCreate / beforeBulkUpdate / beforeBulkDestroy | Bulk ops (instance hooks skip!) |
Pass individualHooks: true to force per-row hooks on bulk ops (expensive — loads all rows).
Quick gotchas
Section titled “Quick gotchas”bulkCreateskipsbeforeCreateby default. Password hashing inbeforeCreate→ plaintext in CSV imports.- Raw queries (
sequelize.query) bypass hooks, paranoid filters, default scopes. - Forgetting
{ transaction: t }runs the query on a separate connection — silent atomicity break. paranoid: truedoesn’t cascade. Orphan FKs after soft delete.uniqueindex sees soft-deleted rows. Need partial uniqueWHERE deleted_at IS NULL(Postgres) or generated-column trick (MySQL).findAndCountAllwith includes inflates count to joined row count. Usedistinct: true.