Sequelize

SequelizeはNode.js向けの歴史あるPromiseベースのORMライブラリです。PostgreSQL、MySQL、MariaDB、SQLite、Microsoft SQL Serverなど主要データベースをサポート。豊富な機能と安定性で9年以上の実績を持ち、Node.jsエコシステムで最も人気のあるORMの一つです。TypeScript対応も強化されています。

ORMJavaScriptTypeScriptNode.jsデータベースPromise

GitHub概要

sequelize/sequelize

Feature-rich ORM for modern Node.js and TypeScript, it supports PostgreSQL (with JSON and JSONB support), MySQL, MariaDB, SQLite, MS SQL Server, Snowflake, Oracle DB (v6), DB2 and DB2 for IBM i.

スター30,112
ウォッチ404
フォーク4,295
作成日:2010年7月22日
言語:TypeScript
ライセンス:MIT License

トピックス

db2-ibm-ifeature-richjavascriptmariadbmicrosoft-sql-servermssqlmssql-servermysqlnodejsoracle-dbormpostgresqlsequelizesnowflakedbsqlsqlitetransactionstypescript

スター履歴

sequelize/sequelize Star History
データ取得日時: 2025/8/13 01:43

ライブラリ

Sequelize

概要

SequelizeはNode.js向けの歴史あるPromiseベースのORMライブラリです。PostgreSQL、MySQL、MariaDB、SQLite、Microsoft SQL Serverなど主要データベースをサポート。豊富な機能と安定性で9年以上の実績を持ち、Node.jsエコシステムで最も人気のあるORMの一つです。TypeScript対応も強化されています。

詳細

Sequelizeは2010年にリリースされたNode.js最古のORMの一つで、長い歴史と豊富な機能セットを誇ります。強力なトランザクション機能、リレーション管理、Eager/Lazy Loading、レプリケーション対応など、エンタープライズレベルのアプリケーション開発に必要な機能を網羅しています。

主な特徴

  • Promise/Async-Await対応: 非同期処理の完全サポート
  • 豊富なデータベースサポート: 9つの主要データベースエンジンに対応
  • マイグレーション機能: スキーマ変更の体系的管理
  • バリデーション機能: 豊富なバリデーター(@sequelize/validator.js)
  • コネクションプール: 効率的なデータベース接続管理
  • TypeScript統合: sequelize-typescriptによるデコレータサポート

メリット・デメリット

メリット

  • Node.jsエコシステムで最も歴史と実績のあるORM
  • 豊富な機能セット(トランザクション、リレーション、マイグレーション)
  • 多数のデータベースエンジンをサポート
  • 活発なコミュニティと豊富なドキュメント
  • エンタープライズレベルの機能(読み取りレプリケーション等)
  • 段階的な導入が可能(既存プロジェクトへの追加)

デメリット

  • TypeScriptの型安全性が他の新しいORMより劣る
  • APIが冗長で学習コストが高い
  • 設定が複雑になりがち
  • パフォーマンスチューニングに専門知識が必要
  • 新しい代替ORM(Prisma等)と比較して開発者体験が劣る
  • 一部の機能でドキュメントが不十分

参考ページ

書き方の例

基本的なセットアップ

npm install @sequelize/core
npm install @sequelize/postgres  # PostgreSQL用
npm install sequelize-typescript  # TypeScript用

モデルの定義(JavaScript)

const { DataTypes } = require('@sequelize/core')

const User = sequelize.define('User', {
  firstName: {
    type: DataTypes.STRING,
    allowNull: false,
    validate: {
      notEmpty: true
    }
  },
  lastName: {
    type: DataTypes.STRING,
    allowNull: false
  },
  email: {
    type: DataTypes.STRING,
    allowNull: false,
    unique: true,
    validate: {
      isEmail: true
    }
  },
  age: {
    type: DataTypes.INTEGER,
    validate: {
      min: 0,
      max: 120
    }
  }
}, {
  timestamps: true,
  paranoid: true,  // 論理削除
  tableName: 'users'
})

module.exports = User

TypeScriptでのモデル定義

import { Table, Column, Model, DataType, PrimaryKey, AutoIncrement, AllowNull, Unique, Validate } from 'sequelize-typescript'

@Table({
  tableName: 'users',
  timestamps: true,
  paranoid: true
})
export class User extends Model {
  @PrimaryKey
  @AutoIncrement
  @Column(DataType.INTEGER)
  id!: number

  @AllowNull(false)
  @Validate({ notEmpty: true })
  @Column(DataType.STRING)
  firstName!: string

  @AllowNull(false)
  @Column(DataType.STRING)
  lastName!: string

  @AllowNull(false)
  @Unique
  @Validate({ isEmail: true })
  @Column(DataType.STRING)
  email!: string

  @Validate({ min: 0, max: 120 })
  @Column(DataType.INTEGER)
  age?: number
}

データベース接続設定

import { Sequelize } from 'sequelize-typescript'
import { User } from './models/User'

const sequelize = new Sequelize({
  database: 'myapp',
  dialect: 'postgres',
  username: 'username',
  password: 'password',
  host: 'localhost',
  port: 5432,
  models: [User],
  logging: console.log,
  pool: {
    max: 10,
    min: 0,
    acquire: 30000,
    idle: 10000
  }
})

// 接続テスト
async function testConnection() {
  try {
    await sequelize.authenticate()
    console.log('データベース接続成功')
  } catch (error) {
    console.error('データベース接続失敗:', error)
  }
}

基本的なCRUD操作

// ユーザー作成
const user = await User.create({
  firstName: '太郎',
  lastName: '田中',
  email: '[email protected]',
  age: 30
})

// ユーザー検索
const allUsers = await User.findAll()
const userById = await User.findByPk(1)
const userByEmail = await User.findOne({
  where: { email: '[email protected]' }
})

// 複数条件での検索
const { Op } = require('@sequelize/core')
const adultUsers = await User.findAll({
  where: {
    age: {
      [Op.gte]: 18
    },
    firstName: {
      [Op.like]: '%太%'
    }
  },
  order: [['createdAt', 'DESC']],
  limit: 10,
  offset: 0
})

// ユーザー更新
await User.update(
  { age: 31 },
  { where: { id: 1 } }
)

// 特定ユーザーの更新
const user = await User.findByPk(1)
user.age = 32
await user.save()

// ユーザー削除
await User.destroy({
  where: { id: 1 }
})

リレーションの定義と操作

import { Table, Column, Model, HasMany, BelongsTo, ForeignKey } from 'sequelize-typescript'

@Table({ tableName: 'posts' })
export class Post extends Model {
  @Column
  title!: string

  @Column(DataType.TEXT)
  content!: string

  @ForeignKey(() => User)
  @Column
  userId!: number

  @BelongsTo(() => User)
  author!: User
}

// Userモデルにリレーション追加
@Table({ tableName: 'users' })
export class User extends Model {
  // ... 他のプロパティ

  @HasMany(() => Post)
  posts!: Post[]
}

// リレーションを含む検索
const userWithPosts = await User.findOne({
  where: { id: 1 },
  include: [Post]
})

// ネストしたリレーション
const postsWithAuthors = await Post.findAll({
  include: [{
    model: User,
    as: 'author',
    attributes: ['firstName', 'lastName', 'email']
  }]
})

高度なクエリとトランザクション

// 複雑なクエリ(JOIN)
const results = await sequelize.query(`
  SELECT u.firstName, u.lastName, COUNT(p.id) as postCount
  FROM users u
  LEFT JOIN posts p ON u.id = p.userId
  WHERE u.age > :age
  GROUP BY u.id
  HAVING COUNT(p.id) > :postCount
`, {
  replacements: { age: 25, postCount: 5 },
  type: QueryTypes.SELECT
})

// トランザクション
const transaction = await sequelize.transaction()

try {
  const user = await User.create({
    firstName: '花子',
    lastName: '山田',
    email: '[email protected]'
  }, { transaction })

  const post = await Post.create({
    title: '最初の投稿',
    content: 'こんにちは、世界!',
    userId: user.id
  }, { transaction })

  await transaction.commit()
  console.log('トランザクション成功')
} catch (error) {
  await transaction.rollback()
  console.error('トランザクション失敗:', error)
}

// 管理されたトランザクション(推奨)
await sequelize.transaction(async (t) => {
  const user = await User.create({
    firstName: '次郎',
    lastName: '佐藤',
    email: '[email protected]'
  }, { transaction: t })

  await Post.create({
    title: '自動トランザクション',
    content: 'エラー時は自動ロールバック',
    userId: user.id
  }, { transaction: t })
})

マイグレーション

# Sequelize CLI インストール
npm install --save-dev @sequelize/cli

# 設定ファイル作成
npx sequelize-cli init

# マイグレーション作成
npx sequelize-cli migration:generate --name create-users-table

# マイグレーション実行
npx sequelize-cli db:migrate

# マイグレーション取り消し
npx sequelize-cli db:migrate:undo
// マイグレーションファイル例
'use strict'

module.exports = {
  async up(queryInterface, Sequelize) {
    await queryInterface.createTable('Users', {
      id: {
        allowNull: false,
        autoIncrement: true,
        primaryKey: true,
        type: Sequelize.INTEGER
      },
      firstName: {
        type: Sequelize.STRING,
        allowNull: false
      },
      lastName: {
        type: Sequelize.STRING,
        allowNull: false
      },
      email: {
        type: Sequelize.STRING,
        allowNull: false,
        unique: true
      },
      age: {
        type: Sequelize.INTEGER
      },
      createdAt: {
        allowNull: false,
        type: Sequelize.DATE
      },
      updatedAt: {
        allowNull: false,
        type: Sequelize.DATE
      },
      deletedAt: {
        type: Sequelize.DATE
      }
    })

    await queryInterface.addIndex('Users', ['email'])
  },

  async down(queryInterface, Sequelize) {
    await queryInterface.dropTable('Users')
  }
}

バリデーションとフック

import { IsEmail, Length } from '@sequelize/validator.js'

@Table({ tableName: 'users' })
export class User extends Model {
  @AllowNull(false)
  @Length([2, 50])
  @Column
  firstName!: string

  @AllowNull(false)
  @IsEmail
  @Column
  email!: string

  // フック(ライフサイクルイベント)
  @BeforeCreate
  static async hashPassword(user: User) {
    if (user.password) {
      user.password = await bcrypt.hash(user.password, 10)
    }
  }

  @AfterCreate
  static async sendWelcomeEmail(user: User) {
    // ウェルカムメール送信ロジック
    console.log(`ウェルカムメールを${user.email}に送信`)
  }

  // インスタンスメソッド
  async validatePassword(password: string): Promise<boolean> {
    return bcrypt.compare(password, this.password)
  }
}