bookshelf vs objection vs sequelize vs waterline
Node.js 環境における ORM ライブラリの比較と選定
bookshelfobjectionsequelizewaterline類似パッケージ:

Node.js 環境における ORM ライブラリの比較と選定

bookshelfobjectionsequelizewaterline は、Node.js 应用程序でデータベース操作を簡素化するための ORM(Object-Relational Mapping)ライブラリです。これらは SQL データベースのテーブルを JavaScript のオブジェクトとして扱えるようにし、クエリ作成やリレーション管理を抽象化します。sequelize は機能豊富で多くのデータベースをサポートし、objectionbookshelf は Knex クエリビルダーを基盤にしています。waterline は Sails.js フレームワークの標準 ORM として知られていますが、近年は保守状況に注意が必要です。

npmのダウンロードトレンド

3 年

GitHub Starsランキング

統計詳細

パッケージ
ダウンロード数
Stars
サイズ
Issues
公開日時
ライセンス
bookshelf06,362-2386年前MIT
objection07,343645 kB1292年前MIT
sequelize030,3522.91 MB1,0231ヶ月前MIT
waterline05,3981.3 MB34-MIT

Node.js ORM 比較:機能、保守性、アーキテクチャ

bookshelfobjectionsequelizewaterline は、Node.js でデータベースを扱うための代表的な ORM ライブラリですが、内部の仕組みや保守状況に大きな違いがあります。フルスタック開発や BFF(Backend for Frontend)層を構築する際、これらの選択はプロジェクトの長期維持性に直結します。主要な違いを技術的に比較します。

🗂️ モデル定義:クラスベース vs 設定オブジェクト

sequelize は定義関数またはクラスを使用し、メタデータが豊富です。

  • 型定義と検証ルールをモデル内に記述できます。
  • 設定項目が多く、学習コストはやや高めです。
// sequelize: Model definition
const User = sequelize.define('User', {
  firstName: { type: DataTypes.STRING, allowNull: false },
  lastName: { type: DataTypes.STRING }
});

objection は ES6 クラスを拡張してモデルを定義します。

  • Knex をベースにしているため、クエリビルダーとの親和性が高いです。
  • tableNamerelationMappings をプロパティとして定義します。
// objection: Model class
class User extends Model {
  static get tableName() {
    return 'users';
  }
}

bookshelf もクラス拡張ですが、より簡素な構造です。

  • bookshelf.Model.extend を使用して定義します。
  • 設定はオブジェクトベースで、シンプルですが機能は限定されます。
// bookshelf: Model extend
const User = bookshelf.Model.extend({
  tableName: 'users'
});

waterline は設定オブジェクト中心の定義です。

  • 属性と型を attributes オブジェクトで宣言します。
  • Sails.js のコンベンションに強く依存しています。
// waterline: Definition object
module.exports = {
  attributes: {
    firstName: { type: 'string', required: true },
    lastName: { type: 'string' }
  }
};

📥 データ取得:クエリビルダーとの連携

sequelize は独自のクエリインターフェースを持ちます。

  • findAllfindOne などのメソッドでデータを取得します。
  • 複雑なクエリもメソッドチェーンで構築可能ですが、独自仕様です。
// sequelize: Fetching data
const users = await User.findAll({
  where: { lastName: 'Smith' }
});

objection は Knex クエリビルダーをそのまま使用します。

  • query() メソッドから開始し、Knex のメソッドを連鎖させます。
  • SQL への親和性が高く、複雑な結合も書きやすいです。
// objection: Fetching data
const users = await User.query().where('lastName', 'Smith');

bookshelf も Knex をベースにしていますが、ラッパーがあります。

  • fetchAllwhere を使用してクエリを構築します。
  • objection に比べると機能追加は緩やかです。
// bookshelf: Fetching data
const users = await User.where('lastName', 'Smith').fetchAll();

waterline は独自のクエリ構文を持ちます。

  • find メソッドを使用し、オブジェクトで条件を指定します。
  • 汎用性はあるものの、生 SQL や複雑な結合では制限が出ることがあります。
// waterline: Fetching data
const users = await User.find({
  where: { lastName: 'Smith' }
});

🔗 リレーション管理:定義と読み込み

sequelize はモデル間で関連を定義し、include で読み込みます。

  • hasManybelongsTo で関係を宣言します。
  • 遅延読み込み(Lazy loading)もサポートしています。
// sequelize: Relations
User.hasMany(Post);
const user = await User.findOne({ include: [Post] });

objectionrelationMappings で関係を定義します。

  • 結合クエリは withGraphFetched で実行します。
  • 循環参照の検出など、高度な機能が備わっています。
// objection: Relations
class User extends Model {
  static get relationMappings() {
    return { posts: { relation: Model.HasManyRelation, modelClass: Post } };
  }
}
const user = await User.query().withGraphFetched('posts');

bookshelfhasMany などで関係を定義します。

  • fetch 時に { withRelated: [...] } オプションで関連データを読み込みます。
  • 仕組みはシンプルですが、複雑なケースでは設定が増えます。
// bookshelf: Relations
const User = bookshelf.Model.extend({
  posts: function() { return this.hasMany(Post); }
});
const user = await User.fetch({ withRelated: ['posts'] });

waterlineattributes 内で関連を定義します。

  • collectionmodel キーで関連付けを行います。
  • 自動的な結合処理が行われますが、制御性は低めです。
// waterline: Relations
module.exports = {
  attributes: {
    posts: { collection: 'post', via: 'userId' }
  }
};
// Usage: User.find().populate('posts')

⚠️ 保守状況と将来性

sequelize は活発に保守されており、バージョン 7 へ移行中です。

  • 企業での採用事例が多く、長期サポートが期待できます。
  • 破壊的変更がある場合はマイグレーションガイドが提供されます。

objection も継続的に更新されており、コミュニティ支持が厚いです。

  • Knex エコシステムの一部として安定しています。
  • TypeScript 定義ファイルも整備されています。

bookshelf は更新頻度が低下しており、注意が必要です。

  • 重要なバグ修正が遅れる可能性があります。
  • 新規プロジェクトでは objection への移行を検討すべきです。

waterline は Sails.js のメンテナンスモードに伴い、リスクが高いです。

  • 新規プロジェクトでの使用は非推奨です。
  • 既存システム以外は、より活性のある ORM へ移行すべきです。

📊 比較サマリー

機能sequelizeobjectionbookshelfwaterline
基盤独自KnexKnex独自
定義スタイル関数/クラスクラスクラスオブジェクト
クエリ制御抽象化重視柔軟性重視標準的制限あり
TypeScript対応良好限定的限定的
保守状況活発活発低下リスク大

💡 最終的な推奨

sequelize は機能完备な工具箱 🧰 のように、大規模で標準化されたプロジェクトに適しています。複数のデータベース対応や、内蔵マイグレーションが必要な場合に最適です。

objection は精密なエンジニアリングキット 🔧 のように、クエリ制御とオブジェクトマッピングのバランスを求める場合に適しています。現代の Node.js 開発において、最もバランスの取れた選択肢の一つです。

bookshelfwaterline は、既存システムの維持以外では使用を避けるべきです — 保守リスクがメリットを上回ります。

結論:新規プロジェクトでは objection または sequelize を選定し、プロジェクトの規模と制御性の要件に合わせて決定してください。

選び方: bookshelf vs objection vs sequelize vs waterline

  • bookshelf:

    過去の資産がある場合を除き、新規プロジェクトでは慎重な検討が必要です。保守頻度が低下しており、コミュニティの支持も objection へ移行しつつあります。既存のレガシーシステムを維持する場合以外は、代替案を検討すべきです。

  • objection:

    Knex クエリビルダーの灵活性を活かしたい開発者に向いています。モデル定義がクラスベースで、TypeScript との相性も良好です。データベーススキーマへの制御を保ちつつ、オブジェクトマッピングの利便性も得たいバランス重視のプロジェクトに最適です。

  • sequelize:

    大規模なエンタープライズプロジェクトや、複数のデータベースエンジンを使い分ける必要がある場合に適しています。機能セットが豊富で、マイグレーションツールも内蔵されているため、包括的な解决方案を求めるチームに向いています。ただし、ライブラリが重くなる傾向があるため、シンプルな用途ではオーバーキルになる可能性があります。

  • waterline:

    Sails.js フレームワークを使用しない限り、新規プロジェクトでの採用は避けるべきです。保守体制が弱まっており、将来のリスクがあります。スタンドアロンの ORM として使用するよりも、フレームワークに縛られない選択肢を検討することをお勧めします。

bookshelf のREADME

bookshelf.js

NPM Version Build Status Dependency Status devDependency Status

Bookshelf is a JavaScript ORM for Node.js, built on the Knex SQL query builder. It features both Promise-based and traditional callback interfaces, transaction support, eager/nested-eager relation loading, polymorphic associations, and support for one-to-one, one-to-many, and many-to-many relations.

It is designed to work with PostgreSQL, MySQL, and SQLite3.

Website and documentation. The project is hosted on GitHub, and has a comprehensive test suite.

Introduction

Bookshelf aims to provide a simple library for common tasks when querying databases in JavaScript, and forming relations between these objects, taking a lot of ideas from the Data Mapper Pattern.

With a concise, literate codebase, Bookshelf is simple to read, understand, and extend. It doesn't force you to use any specific validation scheme, and provides flexible, efficient relation/nested-relation loading and first-class transaction support.

It's a lean object-relational mapper, allowing you to drop down to the raw Knex interface whenever you need a custom query that doesn't quite fit with the stock conventions.

Installation

You'll need to install a copy of Knex, and either mysql, pg, or sqlite3 from npm.

$ npm install knex
$ npm install bookshelf

# Then add one of the following:
$ npm install pg
$ npm install mysql
$ npm install sqlite3

The Bookshelf library is initialized by passing an initialized Knex client instance. The Knex documentation provides a number of examples for different databases.

// Setting up the database connection
const knex = require('knex')({
  client: 'mysql',
  connection: {
    host     : '127.0.0.1',
    user     : 'your_database_user',
    password : 'your_database_password',
    database : 'myapp_test',
    charset  : 'utf8'
  }
})
const bookshelf = require('bookshelf')(knex)

// Defining models
const User = bookshelf.model('User', {
  tableName: 'users'
})

This initialization should likely only ever happen once in your application. As it creates a connection pool for the current database, you should use the bookshelf instance returned throughout your library. You'll need to store this instance created by the initialize somewhere in the application so you can reference it. A common pattern to follow is to initialize the client in a module so you can easily reference it later:

// In a file named, e.g. bookshelf.js
const knex = require('knex')(dbConfig)
module.exports = require('bookshelf')(knex)

// elsewhere, to use the bookshelf client:
const bookshelf = require('./bookshelf')

const Post = bookshelf.model('Post', {
  // ...
})

Examples

Here is an example to get you started:

const knex = require('knex')({
  client: 'mysql',
  connection: process.env.MYSQL_DATABASE_CONNECTION
})
const bookshelf = require('bookshelf')(knex)

const User = bookshelf.model('User', {
  tableName: 'users',
  posts() {
    return this.hasMany(Posts)
  }
})

const Post = bookshelf.model('Post', {
  tableName: 'posts',
  tags() {
    return this.belongsToMany(Tag)
  }
})

const Tag = bookshelf.model('Tag', {
  tableName: 'tags'
})

new User({id: 1}).fetch({withRelated: ['posts.tags']}).then((user) => {
  console.log(user.related('posts').toJSON())
}).catch((error) => {
  console.error(error)
})

Official Plugins

  • Virtuals: Define virtual properties on your model to compute new values.
  • Case Converter: Handles the conversion between the database's snake_cased and a model's camelCased properties automatically.
  • Processor: Allows defining custom processor functions that handle transformation of values whenever they are .set() on a model.

Community plugins

  • bookshelf-cascade-delete - Cascade delete related models on destroy.
  • bookshelf-json-columns - Parse and stringify JSON columns on save and fetch instead of manually define hooks for each model (PostgreSQL and SQLite).
  • bookshelf-mask - Similar to the functionality of the {@link Model#visible} attribute but supporting multiple scopes, masking models and collections using the json-mask API.
  • bookshelf-schema - A plugin for handling fields, relations, scopes and more.
  • bookshelf-signals - A plugin that translates Bookshelf events to a central hub.
  • bookshelf-paranoia - Protect your database from data loss by soft deleting your rows.
  • bookshelf-uuid - Automatically generates UUIDs for your models.
  • bookshelf-modelbase - An alternative to extend Model, adding timestamps, attribute validation and some native CRUD methods.
  • bookshelf-advanced-serialization - A more powerful visibility plugin, supporting serializing models and collections according to access permissions, application context, and after ensuring relations have been loaded.
  • bookshelf-plugin-mode - Plugin inspired by the functionality of the {@link Model#visible} attribute, allowing to specify different modes with corresponding visible/hidden fields of model.
  • bookshelf-secure-password - A plugin for easily securing passwords using bcrypt.
  • bookshelf-default-select - Enables default column selection for models. Inspired by the functionality of the {@link Model#visible} attribute, but operates on the database level.
  • bookshelf-ez-fetch - Convenient fetching methods which allow for compact filtering, relation selection and error handling.
  • bookshelf-manager - Model & Collection manager to make it easy to create & save deep, nested JSON structures from API requests.

Support

Have questions about the library? Come join us in the #bookshelf freenode IRC channel for support on knex.js and bookshelf.js, or post an issue on Stack Overflow.

Contributing

If you want to contribute to Bookshelf you'll usually want to report an issue or submit a pull-request. For this purpose the online repository is available on GitHub.

For further help setting up your local development environment or learning how you can contribute to Bookshelf you should read the Contributing document available on GitHub.

F.A.Q.

Can I use standard node.js style callbacks?

Yes, you can call .asCallback(function(err, resp) { on any database operation method and use the standard (err, result) style callback interface if you prefer.

My relations don't seem to be loading, what's up?

Make sure to check that the type is correct for the initial parameters passed to the initial model being fetched. For example new Model({id: '1'}).load([relations...]) will not return the same as new Model({id: 1}).load([relations...]) - notice that the id is a string in one case and a number in the other. This can be a common mistake if retrieving the id from a url parameter.

This is only an issue if you're eager loading data with load without first fetching the original model. new Model({id: '1'}).fetch({withRelated: [relations...]}) should work just fine.

My process won't exit after my script is finished, why?

The issue here is that Knex, the database abstraction layer used by Bookshelf, uses connection pooling and thus keeps the database connection open. If you want your process to exit after your script has finished, you will have to call .destroy(cb) on the knex property of your Bookshelf instance or on the Knex instance passed during initialization. More information about connection pooling can be found over at the Knex docs.

How do I debug?

If you pass debug: true in the options object to your knex initialize call, you can see all of the query calls being made. You can also pass that same option to all methods that access the database, like model.fetch() or model.destroy(). Examples:

// Turning on debug mode for all queries
const knex = require('knex')({
  debug: true,
  client: 'mysql',
  connection: process.env.MYSQL_DATABASE_CONNECTION
})
const bookshelf = require('bookshelf')(knex)

// Debugging a single query
new User({id: 1}).fetch({debug: true, withRelated: ['posts.tags']}).then(user => {
  // ...
})

Sometimes you need to dive a bit further into the various calls and see what all is going on behind the scenes. You can use node-inspector, which allows you to debug code with debugger statements like you would in the browser.

Bookshelf uses its own copy of the bluebird Promise library. You can read up here for more on debugging Promises.

Adding the following block at the start of your application code will catch any errors not otherwise caught in the normal Promise chain handlers, which is very helpful in debugging:

process.stderr.on('data', (data) => {
  console.log(data)
})

How do I run the test suite?

See the CONTRIBUTING document on GitHub.

Can I use Bookshelf outside of Node.js?

While it primarily targets Node.js, all dependencies are browser compatible, and it could be adapted to work with other javascript environments supporting a sqlite3 database, by providing a custom Knex adapter. No such adapter exists though.

Which open-source projects are using Bookshelf?

We found the following projects using Bookshelf, but there can be more:

  • Ghost (A blogging platform) uses bookshelf. [Link]
  • Soapee (Soap Making Community and Resources) uses bookshelf. [Link]
  • NodeZA (Node.js social platform for developers in South Africa) uses bookshelf. [Link]
  • Sunday Cook (A social cooking event platform) uses bookshelf. [Link]
  • FlyptoX (Open-source Node.js cryptocurrency exchange) uses bookshelf. [Link]
  • And of course, everything on here use bookshelf too.