bookshelf-modelbase-plus
Why
Bookshelf modelbase plugin offers good functionality, however when dealing with a number of similar REST microservices
it is good to have shared codebase for common operations like paginated list, updating the records with validation,
importing set of records and etc.
Install
npm i --save bookshelf-modelbase-plus
Setup
const db = require(knex)(require('./knexfile'));
const bookshelf = require('bookshelf')(db);
const modelbase = require('bookshelf-modelbase-plus');
bookshelf.plugin(require('bookshelf-modelbase-plus'));
const ModelBase = modelbase(bookshelf);
var Budget = ModelBase.extend({
tableName: 'budgets',
validationOptions: {},
});
API
model.getList
var data = {
status: 'enabled',
order_by: '-id',
limit: 5,
page: 1,
}
Budget
.getList(data, ['id', 'name', 'status'])
.then(function(models) {
})
.catch(function(err) { });
Complex Queries:
- Supports Arrays for key operator/value, like
Budget.getList({status: ['=', 'enabled']}, ['status']);
- Supports specifying logic, like
Budget.getList({a: 2, b: 3, _logic: 'or'});
Budget.getList({
status: {
operator: 'LIKE',
value: '%enabled%',
}}).then(models => {});
- Supports array values, like:
Budget.getList({status: ['IN', ['enabled', 'disabled']]}, ['status']);
- Supports or/and nesting, like:
Budget.getList({name: 'name0', _or: {name: 'name1', _and: {status: 'enabled'}}}, ['name', 'status'])
Budget.getList({name: 'name0', _or: [{name: 'name1'}, {status: 'enabled'}]}, ['name', 'status'])
Supported Operators
- !=
- NOT_EQUAL_TO
- <
- LESS_THAN
- <
- LESS_THAN_OR_EQUAL_TO
- >
- GREATER_THAN
- >
- GREATER_THAN_OR_EQUAL_TO
- =
- EQUAL_TO
- IS
- IS_NOT
- LIKE
- NOT_LIKE
- BETWEEN
- NOT_BETWEEN
- IN
- NOT_IN
- MATCH
- NOT_MATCH
- MATCH_BOOL
- NOT_MATCH_BOOL
- MATCH_QUERY
- NOT_MATCH_QUERY
Special queries
A function may optionally be defined on the model to be used when
specifying withQuery
in the getList options, similar to withRelated
.
This function will then be called with the knex query builder so that
the model can define custom code to refine the search
(i.e. join, convert values, etc.). This must avoid the following keywords:
through, fetch, count, fetchOne, create, resetQuery, query, orderBy, sync, clone, _handleResponse, _handleEager
Example:
var Budget = ModelBase.extend({
tableName: 'budgets',
fancy: (qb, options) => {
return qb.where({name: options.name + options.name2});
},
});
Budget.getList({name: 'first', name2: 'last', withQuery: 'fancy'})
Use the optional select
param in the getList options to only return specific columns.
The value should be a comma-separated string or an array of strings.
Use the getListQuery
method to get the underlying getList query without executing
anything. This is useful in case you want to do something custom with the query,
such as stream to a CSV somewhere instead of fetching a list of Bookshelf models.
model.createOne
Budget
.createOne(data, ['id', 'name', 'status'])
.then((model) => {
})
.catch((err) => {
});
model.updateOneById
Budget
.updateOneById(data, id, ['id', 'name', 'status'])
.then((model) => {
});
model.updateOneByCompositePKey
Budget
.updateOneByCompositePKey(data, ['id', 'name', 'status'])
.then((model) => {
});
model.destroyOneByCompositePKey
Budget
.destroyOneByCompositePKey(options)
.then((cnt) => {
});
model.importMany
Budget
.importMany([
{ id: 120, name: 'Test name 00' },
{ id: 139, name: 'Test name 01', status: 'enabled' },
{
name: 'Test name 02', status: 'disabled',
},
],
columns,
(existingModel, updateData) => {
if (existingModel.get('status') === 'deleted' && !updateData.status) {
updateData.status = 'enabled';
existingModel.set('name', null);
return true;
} else {
return false;
}
})
.then((rowCount) => {
res.data = { rows: rowCount };
return next();
})
.catch(err => next(err));
Import Events
Model.eventEmitter.on('import.created', function(createdModel) {...});
Model.eventEmitter.on('import.updated', function(updatedModel, prevModel) {...});
model.destroyMany
Budget
.destroyMany(options)
.then((cnt) => {
});
model.bulkSync
const isMatch = (a, b) => (a.id && a.id === b.id) || (a.type === b.type);
Budget
.fetchAll()
.then(existing => Budget.bulkSync(existing, data, Budget.columns, { isMatch }))
.then((synced) => {
synced.inserted.forEach(i => events.emit('exchangeCreated', i, req));
synced.updated.forEach(i => events.emit('exchangeUpdated', i, req));
synced.destroyed.forEach(i => events.emit('exchangeDestroyed', i, req));
});
model.bulkDestroyIn
Budget
.bulkDestroyIn({id: 1}, {id: 2})
.then(rows => console.log(`deleted ${rows}`));