docs/index.html
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.
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.
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', {
// ...
})
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)
})
.set() on a model.Model#visible attribute but supporting multiple scopes, masking models and collections using the json-mask API.Model, adding timestamps, attribute validation and some native CRUD methods.Model#visible attribute, allowing to specify different modes with corresponding visible/hidden fields of model.Model#visible attribute, but operates on the database level.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.
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.
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.
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.
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.
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)
})
See the CONTRIBUTING document on GitHub.
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.
We found the following projects using Bookshelf, but there can be more:
1.2.0 Jun 07, 2020 - Diff
Collection#fetch: #20791.1.1 Mar 28, 2020 - Diff
morphTo: #2059model.parse() in some cases: #20560.15.2 Mar 28, 2020 - Diff
1.1.0 Jan 31, 2020 - Diff
fetchPage: #20451.0.1 Oct 06, 2019 - Diff
1.0.0 Sep 13, 2019 - Diff
require: true the default for Model#fetch: #20060.15.1 Jun 13, 2019 - Diff
0.15.0 Jun 13, 2019 - Diff
once removes all events after it has been triggered: #19720.14.2 Dec 17, 2018 - Diff
groupBy with table qualifier in pagination plugin: #1928undefined transaction object with Knex 0.15+: #1926.timestamp()'s decision for when to update the updated_at column: #18920.14.1 Dec 09, 2018 - Diff
withRelated fetch option not always grouping properly when using binary primary keys: #19180.14.0 Dec 09, 2018 - Diff
previous() and previousAttributes() methods were changed so that whenever a model is saved or destroyed the previous attributes are no longer reset to the current attributes. Since the old behavior wasn't very useful it's likely this won't cause issues for many people. There's a migration guide in case you are affected by this change. #1848hasOne relation will now return null instead of {} when serialized: #1839. There's a migration guide in the rare event this causes you problems.morphTo data: #1824. There's a migration guide in case you are affected by this.updated_at attribute: #1798. Checkout the migration guide in case you are affected by this.previousAttributes(): #1876rowCount value when using groupBy with fetchPage(): #1852parse/format: #1838timestamp() setting a key named "null" in some cases: #1820withRelated inside events: #1853hasOne's doc: #1890Model.load() relations param: #1834fetchPage(): #1803js and json files: #18830.13.3 Mar 26, 2018 - Diff
updated_at attribute. This was included in a patch release because the chances of any applications depending on this behavior are very small: #17980.13.2 Mar 23, 2018 - Diff
0.13.0 Mar 18, 2018 - Diff
require: true the default when deleting models: #1779saving and creating events to reflect the documentation: #1142returning attribute if client supports returning: #1770idAttribute on save and delete: #1680withSchema option to all database operations: #1638model.id if attributes being .set() contain a parsed version of idAttribute: #1760fetchPage() ignoring or hanging with transactions: #1625fetchPage() from pagination plugin not working for relation collections: #1561idAttribute if it hasn't changed: #1260parentId is not undefined when using fetchAll with relations: #17690.12.1 Jan 8, 2018 - Diff
.detach(): #1720model.has(): #1712fetching:collection and fetched:collection not being generated or visible on the navigation bar: #1114super() on model's initialize(): #1529fetchAll(): #1716previousAttributes for related models: #14570.12.0 Nov 27, 2017 - Diff
Skip visibility-plugin hidden and visible attributes #1699.
<model>.toJSON({ visibility: false })Updated knex peer dependency version to 0.14.x #1694.
Documentation typo fixes #1693.
Now caching node_modules to speed up travis-ci builds #1695.
Use Docker containers for test runs #1674.
Make postpublish work regardless of git remote config #1697.
0.11.1 Nov 15, 2017 — Diff
postinstall
postinstall script can be run as a part of npm prepublish script.0.11.0 Nov 15, 2017 — Diff
Moved .babelrc -> src/.babelrc #1470
Timestamp on save now utilizes a date option for timestamp updates on insert and update. #1592
m.save({item: 'test'}, { date: dateInThePast })Added morphValues for morphTo relation. #1326
Added ability to also set timestamps as model attributes in save.
Removed non-production files from packaging / added them to .npmignore #1679
Development Facing:
0.10.4 - Jul 17, 2017 — Diff
package.json.visible and hidden behavior for toJSON function.belongsTo if foreignKey is null.timestamp function: respect updated_at/created_at being part of the query.fetchPage on Collection (pagination plugin).omitNew=true.0.10.3 - Jan 21, 2017 — Diff
foreignKeyTarget to relation methods.0.10.2 - Sept 22, 2016 — Diff
this.listeners in triggerThen.0.10.1 - Sept 14, 2016 — Diff
0.10.0 — Jun 29, 2016 — Diff
Collection Methods
CollectionBase#collect => use CollectionBase#map insteadCollectionBase#foldl => use CollectionBase#reduce insteadCollectionBase#inject => use CollectionBase#reduce insteadCollectionBase#foldr => use CollectionBase#reduceRight insteadCollectionBase#detect => use CollectionBase#find insteadCollectionBase#select => use CollectionBase#filter insteadCollectionBase#all => use CollectionBase#every insteadCollectionBase#any => use CollectionBase#some insteadCollectionBase#include => use CollectionBase#includes insteadCollectionBase#contains => use CollectionBase#includes insteadCollectionBase#rest => use CollectionBase#tail insteadCollectionBase#invoke => CollectionBase#invokeMapCollectionBase#max into CollectionBase#maxBy - see the lodash docs for more explanationCollectionBase#min into CollectionBase#minBy - see the lodash docs for more explanationModel Methods
ModelBase#pairs => ModelBase#toPairs0.9.5 — May 15, 2016 — Diff
Model#event:fetched on eagerly loaded relations. #1206Model#belongsToMany decorated relations. #12220.9.4 — April 3, 2016 — Diff
babel-runtime as a dependency. #11880.9.3 — April 3, 2016 — Diff
camelCase and colon:separated event names. #11840.9.2 — February 17, 2016 — Diff
peerDependencies.Model.forge works for ES6 classes. #924Collection#count for hasMany relations. #11150.9.1 — November 4, 2015 — Diff
Events#off can now unregister multiple methods at once. #983peerDependencies. #9980.9.0 — November 1, 2015 — Diff
Model#previous returned undefined instead of null for non-existent attributes.null (rather than undefined) is returned from Model#fetch and Collection#fetchOne.Model#idAttribute after successful insert operation. #9550.8.2 — August 20, 2015 — Diff
/src — code is now compiled into /lib via Babel.collection.count, model.count and Model.count.model.refresh. #796fetch and refresh from trying to add JSON attributes to a where clause. #550 #778{patch: true} argument to model.save. #542model.clone and collection.clone, which were not previously working. #744bookshelf.Collection to be modified and extended by plugins (so that relations and fetchAll operations will return the extended instance). #681 #688model.timestamps behavior which deviated from documentation. Also ensure that createdAt is set when {method: "insert"} is passed explicitly. #787create on a through relationship no longer tries to make a pivot object. Previously this would attempt to create an object with invalid foreign keys. #768create in a relation. #7700.8.1 — May 12, 2015 — Diff
initialize not being called in Collection constructor, #737.omitPivot in 0.8 #721serialize, a method which contains toJSON logic for easier customization.0.8.0 — May 1, 2015 — Diff
{require: true} for model.destroy #617belongsToMany, .through #578__super__ internal property on the constructor, this shouldn't have been something you were relying on anyway.0.7.9 — Oct 28, 2014 — Diff
0.7.8 — Oct 28, 2014 — Diff
created_at is now saved with any insert.attaching, attached, detaching, detached #452.0.7.7 — July 23, 2014 — Diff
0.7.6 — June 29, 2014 — Diff
omitPivot flag on toJSON options for omitting the _pivot_ keys in through and belongsToMany relations (#404).0.7.5 — June 23, 2014 — Diff
NotFoundError & EmptyError on Model & Collection, respectively (#389, #399).0.7.4 — June 18, 2014 — Diff
bookshelf.model(name, protoProps, [staticProps]) syntax for registry plugin.0.7.3 — June 17, 2014 — Diff
0.7.2 — June 12, 2014 — Diff
format rather than the original, related to #315.0.7.1 — June 10, 2014 — Diff
0.7.0 — June 9, 2014
Model#fetchAll, for fetching a collection of models from a model.Model#where, as a shortcut for the most commonly used query method.0.6.12 — June 5, 2014 — Diff
belongsTo relation bug with custom parse/format (#377).0.6.11 — June 4, 2014 — Diff
peerDependencies until 0.7 is released to support knex 0.6 and there exists a better internal method of doing a semver check.belongsTo relation bug (#353).0.6.10 — April 3, 2014 — Diff
fetchOne properly resets the query on the collection, (#300).0.6.9 — April 3, 2014 — Diff
0.6.8 — March 6, 2014 — Diff
0.6.7 — March 2, 2014 — Diff
relatedData settings.0.6.6 — March 1, 2014 — Diff
0.6.5 — February 28, 2014 — Diff
Collection#reduceThen as a passthrough to Bluebird's "reduce" method with models.0.6.4 — February 11, 2014 — Diff
Model.collection() as a shortcut for creating a collection with the current model.0.6.3 — February 9, 2014 — Diff
Relation#updatePivot method for updating tables on a "belongsToMany" relation. (#134, #230)created_at or updated_at as timestamp properties. (#158)Plugins:
registry plugin for registering models as strings, helping with the circular dependency problem.virtuals plugin for getting/setting virtual (computed) properties on the model.visibility plugin for specifying a whitelist/blacklist of keys when a model is serialized with toJSON.0.6.2 — December 18, 2013 — Diff
model.id attribute is only set on insert if it's empty. (#130)0.6.1 — November 26, 2013 — Diff
0.6.0 — November 25, 2013 — Diff
0.5.8 — November 24, 2013 — Diff
withRelated fetches to prevent column naming conflicts (#96).0.5.7 — October 11, 2013 — Diff
0.5.6 — October 10, 2013 — Diff
options.query now contains the appropriate knex instance during the "fetching" event handler.0.5.5 — October 1, 2013 — Diff
0.5.4 — October 1, 2013 — Diff
relatedData context was not appropriately maintained for subsequent Collection#create calls after an eager load (#77).Model#related rather than calling a relation method directly, to keep association with the parent model's relations hash.0.5.3 — September 26, 2013 — Diff
columns explicitly specified in a fetch are no-longer passed along when eager loading relations, fixes (#70).0.5.2 — September 22, 2013 — Diff
belongsTo relations (#65).0.5.1 — September 21, 2013 — Diff
hasOne relations (#63).0.5.0 — September 20, 2013 — Diff
Bookshelf.initialize, so you will need to call this once and then reference this Bookshelf client elsewhere in your application.bookshelf.initialize, bookshelf.knex, bookshelf.transaction.options.query inside the event handlers.tableName for all queries, so joins use the correct id (#61).attach & detach now remove models from the associated collection, as appropriate (#59).withPivot no longer accepts an object to specify the keys for the returned pivot items, if you wish to specify how these pivot objects are defined on the object, a custom toJSON is your best bet.Collection#invokeThen and Collection#mapThen as convenience helpers for Promise.all(collection.invoke(method, args*)) and Promise.all(collection.map(method, iterator, [context])), respectively.Bookshelf.plugin method, for a standard way to extend Bookshelf instances.0.3.1 — August 29, 2013 - Diff
belongsToMany custom column name order.0.3.0 — August 28, 2013
Model#through clause on various model relations.{patch: true} flag on save, to only update specific saved attributes.fetchOne method, for pulling out a single model from a collection, mostly useful for related collection instances.morphValue on Model#morphOne or Model#morphMany relations.0.2.8 — August 26, 2013
Sync methods more consistent in their behavior when called directly, (#53).0.2.7 — August 21, 2013
created_at is not set during an "update" query, and the update where clause does not include the idAttribute if it isn't present (#51).0.2.6 — August 21, 2013
0.2.5, mentioned in (#51).0.2.5 — August 19, 2013
Model#query method may now accept a function, for even more dynamic query building (#45).0 as a valid foreign key value (#49).0.2.4 — July 30, 2013
toJSON is only called on a related model if the method exists, allowing for objects or arrays to be manually specified on the relations hash and serialized properly on the parent's toJSON.0.2.3 — July 7, 2013
triggerThen wasn't actually being used for several of the events as noted in 0.2.1 release.0.2.2 — July 2, 2013
The Model's related method is now a no-op if the model doesn't have the related method.
Any withPivot columns on many-to-many relations are now prefixed with _pivot rather than pivot unless named otherwise, for consistency.
The _reset is not called until after all triggered events so that hasChanged can be used on the current model state in the "created", "updated", "saved", and "destroyed" events.
Eager queries may be specified as an object with a function, to constrain the eager queries:
0.2.1 — June 26, 2013
triggerThen instead of trigger for "created", "updated", "saved", "destroyed", and "fetched" events - if any async operations are needed after the model is created but before resolving the original promise.0.2.0 — June 24, 2013
fetch promise with null rather than undefined.query constraints (e.g. {where: {...}, orWhere: {...}}may be passed to the query method (#30).model.relations object.0.1.9 — June 19, 2013
fetch promise with undefined if no model was returned.hasTimestamps.Model#fetch method.exec plugin to provide a node callback style interface for any of the promise methods.0.1.8 — June 18, 2013
morphOne, morphMany, and morphTo model methods.0.1.7 — June 15, 2013
detach may be used with no parameters to detach all related items (#19).0.1.6 — June 15, 2013
idAttribute values to be used in eager loaded many-to-many relations (#18).0.1.5 — June 11, 2013
_previousAttribute and changed values are properly reset on related models after sync actions.0.1.4 — June 10, 2013
idAttribute not being assigned after database inserts.Events methods for clarity.0.1.3 — June 10, 2013
Model#hasChanged, Model#previous, and Model#previousAttributes methods, for getting the previous value of the model since the last sync.Object.create(null) for various internal model objects dealing with user values.Model#related on a model will now create an empty related object if one is not present on the relations object.{patch: true} option on save, instead only applying defaults if the object isNew, or if {defaults: true} is passed.model.clone's relation responses.0.1.2 — May 17, 2013
triggerThen and emitThen for promise based events, used internally in the "creating", "updating", "saving", and "destroying" events.{patch: true} on update to have intended functionality.toJSON is now correctly called on any related properties.0.1.1 — May 16, 2013
belongsTo relations (#14).0.1.0 — May 13, 2013