Sequelize(シークエライズ)は、Node.jsからPostgreSQLやMySQLを操作するためのORMで、2011年からnpmで公開が続いている老舗のライブラリです。2026年10月31日にnpm registryで確認した最新の安定版は6.37.8で、次のv7は別パッケージの@sequelize/coreとしてアルファ版のまま開発が続いています。この記事では、TypeScriptでモデルを定義してCRUDを動かすコード、トランザクションとsequelize-cliによるマイグレーションの作法、2026年に公表された2件のSQLインジェクション、そして既存案件で使い続けるかPrismaやDrizzleへ移るかの判断基準までを扱います。
まとめ:Sequelizeを使い続ける条件とv7・Prisma・Drizzleへ移る境界
Sequelize v6で動いている既存のシステムは、6.37.8へ更新したうえで使い続けて問題ありません。週間ダウンロード数は今も約410万件あり、MySQL・PostgreSQL・SQL Serverなど主要なデータベースに対応し、MITライセンスで無料です。ただし6.37.7以前には2026年に公表されたSQLインジェクションが残っているため、版の確認を最初に行ってください。
新規開発で選ぶ理由は薄くなっています。v6の更新は2025年3月から2026年3月まで1年間止まり、GitHubのREADMEでは次のメジャー版を仕上げる保守者を募集中です。v7のリリース時期は公式に示されていません。TypeScriptで新しく作るなら、型の扱いとマイグレーションの生成で有利なPrismaかDrizzle、既存データベースに型を付けたいならKyselyを先に検討します。
Sequelizeの定義とNode.js向けORMの位置づけ・2026年10月時点の版
最初に、Sequelizeが何をするライブラリで、今どの版を使えばよいのかを整理します。検索で見つかる解説の多くはv5やv6初期の書き方なので、版の状況を押さえておくと古いコードを写す失敗を防げます。
モデルとクエリをJavaScriptで書くORMの設計とv6が対応する8種類のDB
Sequelizeは、テーブルをJavaScriptのクラス(モデル)として定義し、findAllやcreateといったメソッドでSQLを組み立てて実行します。テーブル同士の関連、トランザクション、関連データの先読みを備えた、いわゆるActive Record型のORMです。ORMという仕組み自体の利点と弱点はORMとはで、SQLとの比較とあわせて解説しています。
v6の公式Getting Startedが挙げる接続先は、PostgreSQL・MySQL・MariaDB・SQLite・SQL Server・Db2・Snowflake・Oracleの8種類です。同じモデル定義のまま、dialectの指定とドライバーを差し替えるだけで接続先を変えられます。ドライバーは本体に含まれず、pgやmysql2を別に入れる必要があります。
6.37.8が最新のv6系とアルファ版のまま続くv7系の2026年10月時点の状況
npm registryで2026年10月31日に確認した主な版と、公式のVersioning Policyに載っている動作要件をまとめます。
| パッケージと版 | 公開日 | 扱い |
|---|---|---|
| sequelize 6.37.8 | 2026年3月7日 | 最新の安定版(latest) |
| sequelize 6.37.7 | 2025年3月28日 | 直前の版。2026年の脆弱性の影響あり |
| sequelize 7.0.0-alpha.9 | 2022年2月15日 | npmのalphaタグ(旧パッケージ名のまま) |
| 7.0.0-alpha.48 | 2026年2月4日 | v7最新α版(@sequelize/core) |
| sequelize-cli 6.6.5 | 2026年1月5日 | v6用のマイグレーションCLI |
v6の動作要件はNode.js 10以上・TypeScript 4.1以上で、PostgreSQLは9.5以上、MySQLは5.7系と8.0系です。v7はNode.js 18以上・TypeScript 5.0以上に上がり、PostgreSQLは11以上、MySQLは8.0.19以上が前提になります。注意したいのはnpmのalphaタグです。npm install sequelize@alphaと打つと、2022年2月の7.0.0-alpha.9が入ります。v7の開発は@sequelize/coreという別名のパッケージで進んでおり、試すならこちらを指定してください。
npmでSequelizeを入れてPostgreSQLのモデル定義とCRUDを動かす手順
顧客と受注の2テーブルを例に、接続、型付きのモデル定義、関連を含む検索までのコードを示します。Node.js 20以降、TypeScript、PostgreSQLの組み合わせを想定しています。
インストールと接続設定・authenticateで疎通を確かめるまでのコード
本体とPostgreSQL用のドライバー、マイグレーション用のCLIを入れます。MySQLならpg pg-hstoreの代わりにmysql2を指定してください。
npm install sequelize pg pg-hstore
npm install -D sequelize-cli typescript tsx @types/node
接続はSequelizeのインスタンスを1つ作り、アプリ全体で共有します。authenticate()はSELECT 1+1相当の問い合わせで疎通を確かめるメソッドで、起動時に呼んでおくと接続情報の誤りを早く見つけられます。公式ドキュメントにあるとおり、close()を呼んだインスタンスは再接続できません。既定では実行したSQLがすべてconsole.logに出るため、本番ではloggingオプションでアプリのロガーへ向け直してください。
TypeScriptのInferAttributesで型付きモデルを定義する書き方
公式のTypeScriptガイドは、6.14.0以降で使えるInferAttributesとInferCreationAttributesによる定義を勧めています。クラスのフィールドにdeclareを付けて型だけを宣言し、列の定義はinitに書く形です。
// db.ts:接続とモデル定義をまとめる
import {
Sequelize, Model, DataTypes,
type InferAttributes, type InferCreationAttributes,
type CreationOptional, type ForeignKey,
} from 'sequelize'
export const sequelize = new Sequelize(process.env.DATABASE_URL ?? '', {
dialect: 'postgres',
logging: false, // 既定では全SQLがconsole.logに出る
pool: { max: 10 },
})
export class Customer extends Model<InferAttributes<Customer>, InferCreationAttributes<Customer>> {
declare id: CreationOptional<number> // DBが採番するのでcreate時は省略できる
declare name: string
}
export class Order extends Model<InferAttributes<Order>, InferCreationAttributes<Order>> {
declare id: CreationOptional<number>
declare customerId: ForeignKey<Customer['id']>
declare status: 'received' | 'shipped'
declare amount: number
}
Customer.init({
id: { type: DataTypes.INTEGER, autoIncrement: true, primaryKey: true },
name: { type: DataTypes.STRING, allowNull: false },
}, { sequelize, tableName: 'customers' })
Order.init({
id: { type: DataTypes.INTEGER, autoIncrement: true, primaryKey: true },
status: { type: DataTypes.STRING, allowNull: false },
amount: { type: DataTypes.INTEGER, allowNull: false },
}, { sequelize, tableName: 'orders' })
// 関連を定義すると orders.customerId 列が外部キーとして扱われる
Customer.hasMany(Order, { foreignKey: 'customerId', as: 'orders' })
Order.belongsTo(Customer, { foreignKey: 'customerId', as: 'customer' })
declareを外して普通のクラスフィールドにすると、Sequelizeが用意する属性のゲッターとセッターが隠れてしまい、値が読めなくなります。公式もdeclareを必須としている点です。公式のModel Basicsによると、tableNameを省略するとモデル名が自動で複数形になり、createdAtとupdatedAtの2列も既定で付きます。日時はSequelize側で入れるため、別システムが直接SQLで書き込んだ行には入りません。
findAllとincludeで関連テーブルを読みN+1を避けるクエリ
先ほどのモデルを使って、顧客と受注を登録し、受付中の受注を顧客ごとにまとめて取得します。
// main.ts:登録と関連を含む検索(npx tsx main.ts)
import { sequelize, Customer, Order } from './db.js'
await sequelize.authenticate() // 接続できなければここで例外になる
const c = await Customer.create({ name: '株式会社サンプル' })
await Order.create({ customerId: c.id, status: 'received', amount: 12000 })
// includeを付けると顧客と受注をJOINした1回のSQLで取得する
const customers = await Customer.findAll({
include: [{ model: Order, as: 'orders', where: { status: 'received' }, required: false }],
order: [['id', 'ASC']],
})
for (const row of customers) {
console.log(row.name, row.get('orders'))
}
await sequelize.close()
includeを書かずに、取得した顧客ごとにOrder.findAllを呼ぶと、顧客の件数だけSQLが増えるN+1の形になるため注意が必要です。required: falseは受注が無い顧客も残すLEFT JOINの指定で、外すとINNER JOINになり受注の無い顧客が結果から消えます。関連の定義と読み込みの種類は公式のAssociationsに、ORMごとのN+1の検出方法はN+1問題とはにまとめています。
Sequelizeのトランザクションとsequelize-cliのマイグレーション運用
Sequelizeで事故が起きやすいのは、トランザクションの範囲とスキーマ変更の進め方です。どちらも書き方の作法を知っていれば防げます。
マネージドトランザクションでtransactionオプションを渡し忘れる失敗
公式のTransactionsは、コールバックを渡す「マネージド」と、自分でcommitとrollbackを呼ぶ「アンマネージド」の2種類を説明しています。通常の選択肢はマネージドです。コールバックが例外を投げればロールバック、正常に終われば自動でコミットされます。
import { sequelize, Customer, Order } from './db.js'
await sequelize.transaction(async (t) => {
const c = await Customer.create({ name: '株式会社サンプル' }, { transaction: t })
await Order.create(
{ customerId: c.id, status: 'received', amount: 8000 },
{ transaction: t }, // これを書き忘れると別の接続で実行される
)
// ここで例外を投げると、上の2件はどちらも取り消される
})
v6で一番多い失敗は、{ transaction: t }の書き忘れです。付け忘れたクエリはトランザクションの外で実行されるため、途中で例外が起きても取り消されません。プールの接続数が少ない環境では、トランザクションが接続を握ったまま別の接続を待ち、処理が止まる場合もあるため、接続待ちに注意が必要です。v6でこれを防ぐには、cls-hookedの名前空間を作ってSequelize.useCLS()に渡します。すると、コールバック内のクエリにトランザクションが自動で引き継がれる仕組みです。分離レベルはREAD UNCOMMITTEDからSERIALIZABLEまでの4種類を、トランザクションごとにisolationLevelで指定できます。
db:migrateとup・downのマイグレーションで本番のスキーマを変える手順
公式のMigrationsに沿って、sequelize-cliで変更履歴をファイルとして管理します。initでconfig・models・migrations・seedersの4フォルダができ、migration:generateで日時付きのひな形が作られます。
npx sequelize-cli init
npx sequelize-cli migration:generate --name create-customers-orders
npx sequelize-cli db:migrate # 未適用のファイルを順に流す
npx sequelize-cli db:migrate:undo # 直近の1件を取り消す
// migrations/20261031000000-create-customers-orders.js
'use strict'
module.exports = {
async up(queryInterface, Sequelize) {
await queryInterface.createTable('customers', {
id: { type: Sequelize.INTEGER, autoIncrement: true, primaryKey: true },
name: { type: Sequelize.STRING, allowNull: false },
createdAt: { type: Sequelize.DATE, allowNull: false },
updatedAt: { type: Sequelize.DATE, allowNull: false },
})
await queryInterface.createTable('orders', {
id: { type: Sequelize.INTEGER, autoIncrement: true, primaryKey: true },
customerId: {
type: Sequelize.INTEGER, allowNull: false,
references: { model: 'customers', key: 'id' }, onDelete: 'RESTRICT',
},
status: { type: Sequelize.STRING, allowNull: false },
amount: { type: Sequelize.INTEGER, allowNull: false },
createdAt: { type: Sequelize.DATE, allowNull: false },
updatedAt: { type: Sequelize.DATE, allowNull: false },
})
},
async down(queryInterface) {
await queryInterface.dropTable('orders')
await queryInterface.dropTable('customers')
},
}
適用済みのファイル名はSequelizeMeta表に記録され、db:migrateは記録に無いものだけを流します。モデルのinitとマイグレーションは自動では連動しません。列を足したら、マイグレーションとモデルの両方を同じコミットで直す運用にしてください。接続情報はconfig/config.jsonに直接書かず、use_env_variableで環境変数名を指定する形にすると、パスワードをリポジトリに残さずに済みます。
syncのalterオプションを本番で使わない理由と公式ドキュメントの推奨
sequelize.sync()は、モデルの定義に合わせてテーブルを作る機能です。force: trueはテーブルを削除して作り直し、alter: trueは今のテーブルとモデルの差分を見て列を変更します。開発中の検証用データベースでは手早く便利ですが、公式のModel Basicsは、これらを本番向けのソフトウェアでは勧めないと明記し、マイグレーションを使うよう案内しています。
理由は、変更の中身が事前にレビューできず、取り消しの手順も残らないからです。列名を変えたつもりが「旧列の削除と新列の追加」として実行され、既存のデータが消える事故が典型として挙げられます。syncは自動テストの初期化に限り、本番とステージングではマイグレーションだけを流す、と役割を分けておくのが安全です。
2026年に公表された2件のSQLインジェクションと6.37.8へ上げる理由
ORMを使っていれば値はエスケープされる、という前提は一部の書き方で崩れます。2026年には、v6に深刻度の高い脆弱性が2件公表されました。
JSON列のキャスト型とOracle方言で見つかった脆弱性の範囲と修正版
GitHub Advisory Databaseで2026年10月31日に確認した内容です。
| 識別子 | 公表日 | 影響する版 | 修正版 | 条件 |
|---|---|---|---|---|
| CVE-2026-30951 | 3月11日 | 6.0.0-beta.1〜6.37.7 | 6.37.8 | JSON列への条件 |
| CVE-2026-69240 | 8月3日 | 6.37.4未満 | 6.37.4 | Oracle方言のみ |
CVE-2026-30951は、JSON列の条件に書いたキー名のうち::より後ろが、型名として検証されずにCASTのSQLへ入る問題です。PostgreSQL・MySQL・MariaDB・SQLiteが対象で、v7の@sequelize/coreは影響を受けないとされています。CVE-2026-69240は、Oracle方言でTO_DATEやTO_TIMESTAMPで始まる文字列がエスケープされずにそのまま埋め込まれる問題で、CVSSは9.8でした。どちらも6.37.8で解消されています。
# 間接依存も含めて入っている版を確認し、6.37.8へ上げる
npm ls sequelize
npm install [email protected]
リクエストのオブジェクトをwhereへそのまま渡す書き方が危ない理由
CVE-2026-30951の勧告に載っている攻撃例は、where: { metadata: req.body.filter }のように、リクエストの本文をオブジェクトのまま条件へ渡すコードです。値だけでなくキー名まで利用者が決められるため、細工したキーがSQLに入りました。
版を上げたうえで、書き方の側でも入口を閉じます。条件に使うキーはコード側で固定し、利用者の入力は値として渡すことです。並べ替えの列のように入力で切り替えたい場合は、許可する列名の一覧と照合してから使います。sequelize.literal()やsequelize.query()に入力を文字列連結で渡していないかも、レビューで毎回確かめる観点にしてください。攻撃の典型例とテスト方法はSQLインジェクションとはで解説しています。
Sequelizeを続けるかPrisma・Drizzle・Kyselyへ移るかの判断基準
ここからは、既存案件と新規案件のそれぞれで、Sequelizeをどう扱うかを判断します。結論は「既存は続ける、新規では選ばない」です。
v7アルファと保守者募集から読むSequelizeの開発体制とリスク
GitHubのREADMEには「Seeking New Maintainers for Sequelize!」という節があり、次のメジャー版を完成させる保守者を、四半期ごとに2,500ドルの分配つきで募集しています。v7は2022年からアルファ版が出続け、Versioning Policyでもリリース日とv6のサポート終了日は未定のままです。v6の更新も6.37.7から6.37.8まで約1年空きました。
一方で、すぐに使えなくなる状況ではありません。2026年10月23日から29日の週間ダウンロード数はsequelizeが4,105,790件、@sequelize/coreが210,901件で、利用者の大半はv6に留まっています。脆弱性への修正版も2026年3月に出ました。読み取れるのは「v6の保守は続いているが、次の世代の仕上がりは見通せない」という状態です。
v7へ上げる作業も軽くありません。公式のUpgrade to v7には、パッケージの分割からデータ型、関連名の既定値まで、破壊的変更が数十項目並びます。アルファ版のうちに本番を移す理由はありません。
既存のv6案件を6.37.8に固定してSequelizeを使い続ける採用条件
次の3つがそろう既存システムは、6.37.8へ上げて使い続けるのが妥当です。1つ目は、モデルとマイグレーションが整っていて、機能追加の大半が既存テーブルへの列追加で済むこと。2つ目は、Node.jsの版を18以上に上げられる見込みがあり、将来v7が安定版になったときに追随できること。3つ目は、テストがデータアクセス層を通っていて、版を上げた影響を自動で確かめられることです。
この場合、ORMの置き換えに工数を割くより、{ transaction: t }の書き忘れをuseCLSで塞ぎ、syncを本番の起動処理から外し、JSON列の条件に入力をそのまま渡していないかを点検するほうが、事故を減らす効果は大きくなります。package.jsonでは"sequelize": "6.37.8"のように版を固定し、更新は脆弱性の公表に合わせて行います。
新規開発でSequelizeを選ばない場面と移行先の選び分け
TypeScriptで新しくAPIサーバーを作る案件では、Sequelizeを選びません。型はモデルのクラスとinitの二重定義になり、マイグレーションの生成も持たないため、同じ情報を3か所で保守することになるからです。移行先は次のように選び分けます。
| 観点 | Sequelize v6 | Prisma | Drizzle ORM | Kysely |
|---|---|---|---|---|
| スキーマの正 | モデルのinit | schema.prisma | TSのスキーマ定義 | DBの実物 |
| 型の作り方 | declareで手書き | 定義から生成 | 定義から推論 | 手書きか生成 |
| マイグレーション | CLIで手書き | 差分から生成 | 差分から生成 | 手書き |
| 関連の読み込み | include | include | relations定義 | JSON集約を自分で書く |
モデル中心でCRUDを素早く作るならPrisma、SQLに近い書き味で型とマイグレーションを両方得たいならDrizzle、既存のデータベースを正としたままSequelizeから段階的に抜けたいならKyselyが候補です。PrismaとTypeORMの違いはTypeORMとPrismaの違いと選び方、Prismaの版の状況はPrismaのバージョン一覧、DrizzleはDrizzle ORMとは、KyselyはKyselyとはで解説しています。
既存のSequelizeから移すときは、全面的な書き換えより、新しい機能から別のライブラリで書き、同じデータベースを2つのデータアクセス層で共有する期間を設けるほうが安全です。その間もスキーマ変更はどちらか一方のマイグレーションに寄せておきます。Node.jsのAPIサーバーのデータアクセス層の見直しや、ORMの置き換えを含む改修を進めたい場合は、API開発・システム連携として現状の調査から相談いただけます。
よくある質問:Sequelizeの対応DB・無料利用・TypeORMとの違い・v7移行
Sequelizeの導入や保守でよく出る質問に、2026年10月31日時点の公式情報をもとに答えます。
SequelizeはMySQLとPostgreSQLのどちらでも同じコードで動きますか?
モデルの定義と基本的な検索・登録は、同じコードのままdialectとドライバーを替えるだけで動きます。ただし、JSONB型や配列型、RETURNINGのようにデータベース固有の機能を使った部分は移植できません。Versioning Policyではv6の対応版をPostgreSQL 9.5以上、MySQL 5.7系と8.0系としています。開発をSQLite、本番をPostgreSQLにする構成は、型の差で本番だけ失敗しやすいため避けます。
Sequelizeは商用の業務システムでも無料で使えますか?
使えます。SequelizeはMITライセンスで公開されており、有償版や機能制限版はありません。社内システムでも顧客向けのサービスでも同じ条件で、ライセンス料はかかりません。MITライセンスの条件として、配布物に著作権表示とライセンス文を含めてください。マイグレーション用のsequelize-cliも同じくOSSとして公開されています。
SequelizeとTypeORMの違いは何ですか?
どちらもモデル(エンティティ)を中心にしたNode.js向けのORMですが、定義の書き方が違います。TypeORMはTypeScriptのデコレーターでクラスに列を宣言し、Sequelizeはinitに列の定義を渡す形です。TypeORMは型との結びつきが強く、Sequelizeは素のJavaScriptでも書きやすいという差があります。新規に選ぶ場合は、TypeORMとあわせてPrismaも比較対象に入れてください。
v6からv7へ上げるときに何を直す必要がありますか?
主な作業は、読み込み元をsequelizeから@sequelize/coreへ変えること、@sequelize/postgresのような方言別パッケージを入れること、useCLSの呼び出しを外すこと、アンマネージドのトランザクションをstartUnmanagedTransaction()へ書き換えることです。BIGINTとDECIMALが文字列で返る変更は、計算している箇所すべてに影響します。v7はアルファ版なので、本番での移行は安定版の公開を待ってください。
Sequelizeのマイグレーションを取り消すにはどうしますか?
npx sequelize-cli db:migrate:undoで直近の1件、db:migrate:undo:allですべてを取り消します。特定のファイルまで戻すときは--toにファイル名を指定します。取り消しで実行されるのは各ファイルのdownなので、downが空のファイルは戻せません。列を削除するマイグレーションはdownで列を戻してもデータは戻らないため、本番では事前にバックアップを取ってから流してください。
関連記事
- ORMとは?O/Rマッパーの仕組み・メリットとデメリットをSQL比較と実行結果で解説:SequelizeのようなORMの仕組みと弱点をSQLと比べています
- TypeORMとPrismaの違いと選び方|型安全・発行SQLを実測【2026年10月】:Sequelizeの移行先になるORM同士の違いを発行SQLで比べています
- Drizzle ORM(ドリズル)とは?使い方・Prismaとの比較・v1.0 RCの変更点【2026年10月】:SQLに近い書き味で型とマイグレーションを持つ移行先です
- Kyselyとは:0.29系の導入コードとマイグレーション・Prisma/Drizzleとの選び分け:既存DBを正としたまま段階的に移るときの候補を扱う同グループの記事です
- N+1問題とは?発行クエリ数の実測と検出方法・ORM別の対策:includeで避けるN+1の起き方と検出方法です