bookshelf vs objection vs sequelize vs typeorm
Node.js 项目中的 ORM 框架选型指南
bookshelfobjectionsequelizetypeorm类似的npm包:

Node.js 项目中的 ORM 框架选型指南

bookshelfobjectionsequelizetypeorm 都是 Node.js 生态中流行的 ORM(对象关系映射)库,用于简化数据库操作。sequelizetypeorm 功能全面,支持多种数据库,适合大型项目。objection 基于 SQL 查询构建器,灵活性高。bookshelf 较老,基于 Knex.js,但维护频率较低。这些工具帮助开发者用 JavaScript 对象操作数据库,避免直接编写 SQL。

npm下载趋势

3 年

GitHub Stars 排名

统计详情

npm包名称
下载量
Stars
大小
Issues
发布时间
License
bookshelf06,361-2386 年前MIT
objection07,344645 kB1292 年前MIT
sequelize030,3522.91 MB1,0231 个月前MIT
typeorm036,44320.8 MB5285 个月前MIT

Node.js ORM 框架深度对比:Bookshelf vs Objection vs Sequelize vs TypeORM

在 Node.js 后端开发中,选择合适的 ORM(对象关系映射)框架直接影响项目的可维护性和开发效率。bookshelfobjectionsequelizetypeorm 是四个主流选择,但它们的设计理念和适用场景差异很大。本文将从实际工程角度,对比它们的核心特性。

⚠️ 维护状态与风险提示

bookshelf 目前处于维护模式,更新频率较低。

  • 社区活跃度下降,新功能支持有限。
  • 适合旧项目维护,新项目不建议使用。
// bookshelf: 示例代码(注意:社区支持减少)
const Bookshelf = require('bookshelf')(knex);
const User = Bookshelf.Model.extend({ tableName: 'users' });

objection 维护良好,基于 Knex.js。

  • 灵活性高,适合复杂查询。
  • 文档清晰,社区活跃。
// objection: 示例代码
const { Model } = require('objection');
class User extends Model {
  static get tableName() {
    return 'users';
  }
}

sequelize 稳定且功能丰富。

  • 支持多种数据库,企业级首选。
  • 文档完善,社区庞大。
// sequelize: 示例代码
const { Sequelize, Model } = require('sequelize');
class User extends Model {}
User.init({ username: Sequelize.STRING }, { sequelize, tableName: 'users' });

typeorm 现代且支持 TypeScript。

  • 支持装饰器语法,开发体验好。
  • 与 NestJS 等框架集成良好。
// typeorm: 示例代码
import { Entity, PrimaryGeneratedColumn, Column } from 'typeorm';
@Entity()
export class User {
  @PrimaryGeneratedColumn()
  id: number;
  @Column()
  username: string;
}

🗂️ 模型定义方式:配置 vs 装饰器

bookshelf 使用简单的对象扩展。

  • 基于 Knex.js,定义直观。
  • 适合习惯传统 JavaScript 的开发者。
// bookshelf: 模型定义
const User = Bookshelf.Model.extend({
  tableName: 'users',
  hasTimestamps: true
});

objection 使用类继承。

  • 静态方法定义表名和关系。
  • 代码结构清晰。
// objection: 模型定义
class User extends Model {
  static get tableName() {
    return 'users';
  }
  static get relationMappings() {
    return { posts: { model: Post, relation: Model.HasManyRelation } };
  }
}

sequelize 使用初始化方法或类定义。

  • 支持两种风格,灵活。
  • 配置选项丰富。
// sequelize: 模型定义
class User extends Model {}
User.init({
  username: DataTypes.STRING
}, {
  sequelize,
  tableName: 'users'
});

typeorm 使用装饰器。

  • TypeScript 友好,类型安全。
  • 代码简洁,元数据丰富。
// typeorm: 模型定义
@Entity('users')
export class User {
  @PrimaryGeneratedColumn()
  id: number;
  @Column()
  username: string;
}

📥 数据查询:链式调用 vs 方法调用

bookshelf 使用链式查询。

  • 基于 Knex.js 风格。
  • 适合习惯 SQL 构建器的开发者。
// bookshelf: 查询数据
User.where({ username: 'alice' }).fetch()
  .then(user => console.log(user));

objection 使用查询构建器。

  • 功能强大,支持复杂查询。
  • 语法接近 Knex.js。
// objection: 查询数据
const user = await User.query().findOne({ username: 'alice' });

sequelize 使用内置方法。

  • 方法丰富,如 findAllfindOne
  • 支持复杂条件对象。
// sequelize: 查询数据
const user = await User.findOne({ where: { username: 'alice' } });

typeorm 使用 Repository 模式。

  • 通过 getRepository 获取操作对象。
  • 支持查询构建器。
// typeorm: 查询数据
const user = await userRepository.findOne({ where: { username: 'alice' } });

🔗 关系处理:显式定义 vs 自动推断

bookshelf 需要显式定义关系。

  • 在模型中定义 hasManybelongsTo
  • 查询时使用 withRelated
// bookshelf: 关系查询
User.where({ id: 1 }).fetch({ withRelated: ['posts'] })
  .then(user => console.log(user.related('posts')));

objection 使用 relationMappings

  • 定义清晰,支持 eager loading。
  • 查询时使用 withGraphFetched
// objection: 关系查询
const user = await User.query()
  .findById(1)
  .withGraphFetched('posts');

sequelize 使用 include 选项。

  • 在查询时定义关联。
  • 支持嵌套关联。
// sequelize: 关系查询
const user = await User.findOne({
  where: { id: 1 },
  include: [{ model: Post }]
});

typeorm 使用 Join 查询。

  • 通过 createQueryBuilderfind 选项。
  • 类型安全,自动补全。
// typeorm: 关系查询
const user = await userRepository.findOne({
  where: { id: 1 },
  relations: ['posts']
});

🔄 迁移管理:内置工具 vs 外部库

bookshelf 无内置迁移工具。

  • 需配合 Knex.js 迁移功能。
  • 配置稍显繁琐。
// bookshelf: 迁移(使用 Knex)
// knex migrate:make create_users_table

objection 无内置迁移工具。

  • 推荐配合 Knex.js。
  • 灵活性高,但需手动配置。
// objection: 迁移(使用 Knex)
// knex migrate:make create_users_table

sequelize 内置 CLI 迁移工具。

  • 命令简单,如 sequelize-cli
  • 自动生成迁移文件。
// sequelize: 迁移命令
// npx sequelize-cli migration:generate --name create-users

typeorm 内置迁移工具。

  • 支持自动生成和手动编写。
  • 与实体类同步方便。
// typeorm: 迁移命令
// npm run typeorm -- migration:generate -- -n CreateUsers

🌐 数据库支持:广泛 vs 特定

bookshelf 支持 Knex.js 支持的数据库。

  • 包括 MySQL、PostgreSQL、SQLite 等。
  • 依赖 Knex.js 的驱动。
// bookshelf: 数据库配置
const knex = require('knex')({ client: 'mysql', connection: { ... } });

objection 支持 Knex.js 支持的数据库。

  • 与 Bookshelf 类似,依赖 Knex。
  • 配置灵活。
// objection: 数据库配置
const knex = require('knex')({ client: 'postgresql', connection: { ... } });

sequelize 原生支持多种数据库。

  • MySQL、PostgreSQL、SQLite、MSSQL、MariaDB。
  • 无需额外配置构建器。
// sequelize: 数据库配置
const sequelize = new Sequelize('database', 'username', 'password', {
  dialect: 'postgres'
});

typeorm 支持多种数据库。

  • MySQL、PostgreSQL、SQLite、Oracle、MongoDB 等。
  • 驱动丰富,配置统一。
// typeorm: 数据库配置
const dataSource = new DataSource({
  type: 'postgres',
  host: 'localhost',
  // ...
});

📊 总结对比表

特性bookshelfobjectionsequelizetypeorm
维护状态⚠️ 低频维护✅ 活跃✅ 稳定✅ 活跃
模型定义对象扩展类继承类/初始化装饰器
查询风格链式调用查询构建器方法调用Repository
TypeScript❌ 支持一般✅ 支持良好✅ 支持良好✅ 原生支持
迁移工具需 Knex需 Knex内置 CLI内置 CLI
数据库Knex 支持Knex 支持多种原生多种原生

💡 选型建议

bookshelf 适合旧项目维护,新项目不推荐。

  • 如果你正在维护一个基于 Bookshelf 的旧系统,可以继续使用该框架。
  • 但如果是新项目,建议考虑其他更活跃的选项。

objection 适合需要灵活查询的场景。

  • 如果你的项目需要复杂的 SQL 查询,Objection 是很好的选择。
  • 它基于 Knex.js,既保留了 SQL 的灵活性,又提供了 ORM 的便利。

sequelize 适合企业级稳定项目。

  • 如果你需要一个经过时间考验、文档完善的 ORM,Sequelize 是安全的选择。
  • 它支持多种数据库,社区庞大,遇到问题容易找到解决方案。

typeorm 适合 TypeScript 项目和现代架构。

  • 如果你使用 TypeScript 或 NestJS,TypeORM 是最佳选择。
  • 它的装饰器语法和 Repository 模式让代码更简洁、类型更安全。

🏁 最终结论

这四个框架各有优劣,选择的关键在于你的项目需求和技术栈。

  • 追求稳定和生态:选 sequelize
  • 追求 TypeScript 体验:选 typeorm
  • 追求查询灵活性:选 objection
  • 旧项目维护:可继续用 bookshelf,但新项目避免使用。

无论选择哪个,都要注意数据库设计的合理性,ORM 只是工具,良好的架构才是核心。

如何选择: bookshelf vs objection vs sequelize vs typeorm

  • bookshelf:

    如果你的项目依赖旧版代码库且已稳定运行,可以继续使用 bookshelf。但在新项目中不建议选择,因为其社区活跃度下降,新功能支持有限。它基于 Knex.js,适合喜欢 SQL 查询构建器风格的开发者。

  • objection:

    选择 objection 如果你需要灵活的 SQL 查询能力,同时想要 ORM 的便利。它基于 Knex.js,适合复杂查询场景。社区维护良好,文档清晰,适合中大型项目。

  • sequelize:

    选择 sequelize 如果你需要稳定的、功能丰富的 ORM,支持多种数据库(MySQL、PostgreSQL 等)。它适合企业级应用,文档完善,社区庞大。如果你重视长期支持和稳定性,这是安全的选择。

  • typeorm:

    选择 typeorm 如果你使用 TypeScript,或者喜欢装饰器语法。它支持 Active Record 和 Data Mapper 模式,适合现代 Node.js 项目。与 NestJS 等框架集成良好,适合追求开发体验的团队。

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.