boldr-orm
v0.1.7
Published
ORM for Boldr on top of ObjectionJS with additional utilities for GraphQL
Downloads
3
Readme
boldr-orm
ORM for Boldr on top of ObjectionJS with additional utilities for GraphQL
ObjectionJS documentation: http://vincit.github.io/objection.js/
ObjectionJS Repo: https://github.com/Vincit/objection.js/
Extra Features
Boldr ORM (BORM) adds additional functionality on top of ObjectionJS.
- Automatically added timestamps
- Table name generated from model name automatically
- Soft delete functionality
- GraphQL helpers
Automatic Timestamps
By default all models are assumed to reference tables with createdAt
and updatedAt
fields. These fields are managed by Boldr ORM and updated whenever data is added or removed.
To disable time stamps:
Add static addTimestamps = false;
on to your model.
Two additional helper methods are included for times when you might not want to modify the timestamp.
modify
: updates the updatedAt
column of the table to current time (new Date().toISOString()
).dontModify
: keeps the current timestamps in place when the query is executed.
Default Table Name
Leave the table name generation to the ORM if you're feeling like it.
class Article extends Model {
static tableName = 'Article';
...
}
class Article extends Model {
...
}
Soft Delete
To enable soft delete functionality, add static softDelete = true;
to the model.
Don't forget your table must contain a deletedAt
column, as a nullable timestamp.
Soft delete enabled models, aren't actually removed when the delete method is called. Instead the value of the deletedAt
column is filled with the current timestamp. Queries don't return data which have a timestamp in the deletedAt field.
Helper methods implemented for Soft Delete:delete
: archive or delete a row (depending on whether softDelete is true or false)archive
: archive (soft delete) a row (use delete
it's the same thing)withArchived
: return all data regardless of whether or not they been "deleted" or archived.onlyArchived
: return only the data which has been deleted with softDelete enabled.
restore
: restore (unarchive) an archived set of data.forceDelete
: actually delete a row from the database
If you want to use some other column name instead of deletedAt
you can do static softDelete = 'deleted_at';
class Post extends Model {
// Enable soft delete
static softDelete = true;
}
// soft delete
Post.query().where('id', 1).delete();
// restore an archived row
Post.query().where('id', 1).restore();
// get all non-archived posts
Post.query();
// get all posts (including archived / "soft deleted" ones)
Post.query().withArchived();
// retrieve only archived posts
Post.query().onlyArchived();
// completely remove a post from the database
Post.query().forceDelete();
GraphQL Helpers
getDeleteByIdResolver()
returns a resolver for GraphQL delete mutation
deleteCategory(id: ID) { DeletedItem }
Mutation: { deleteCategory: Category.getDeleteByIdResolver() }
getFindByIdSubResolver()
returns a resolver for finding model by id. It optionally takes a propName
argument which denotes the name of the field containing the id.
type Store { id: ID!, name: String!, category: Category}
Query: { Store: { category: Category.getFindByIdSubResolver() } }
Query: { Store: { category: Category.getFindByIdSubResolver('categoryId') } }
save
and saveAndFetch
save
: inserts a model if the id column does not exist, otherwise updates it.saveAndFetch
: saves the model and then fetches it.
wrapWhere
Wraps the where condition till now into braces
so builder.where('a', 'b').orWhere('c', 'd').wrapWhere().where('e', 'f');
becomes "WHERE (a = 'b' OR c = 'd') AND e = 'f'"
whereByOr(obj)
creates an and (or - or - or) condition
q.where('id', 1).whereByOr({posts: 100, user: 'admin'})
// where `id` = 1 and (`posts` = 100 or `user` = 'admin')
whereByAnd(obj)
creates an or (and - and - and) condition
q.where('id', 1).whereByAnd({posts: 100, user: 'admin'})
// where `id` = 1 or (`posts` = 100 and `user` = 'admin')
find Method
find
: find is like where except if only a single argument is given, it treats the argument as an id.
// all three are equivalent
Person.query().find(1);
Person.query().where('id', 1);
Person.query().findById(1);
// both are equivalent
Person.query().find('name', 'John');
Person.query().where('name', 'John');
where and find methods on model
Instead of doing Person.query().where()
you can do Person.where()
Instead of doing Person.query().find()
you can do Person.find()
Easier Relationships
You can define all your relationships in the $relations
method.
Methods for defining relations
belongsTo(model, options) hasOne(model, options) hasMany(model, options) hasManyThrough(model, options)
options are totally optional. they can be:
name: name of the relationship (by default name of the model in camelCase)
joinFrom: join field from (by default ModelTableName.camelCasedModelNameId)
joinTo: join field to (by default ModelTableName.id)
filter: apply this filter to the model
through: {
model: if there is a model for the through table
table: tableName of the table to join through
from: join through from
to: join through to
filter: filter to apply on the join table
extra: extra fields to select from the through table
}
class Pet extends Model {
static $relations() {
this.hasOne(Pet);
this.hasMany(Toy, {name: 'toys'});
this.belongsTo(Person);
this.hasManyThrough(Pet, {
name: 'siblings',
through: {
table: 'Pet_Siblings',
from: 'pet_id',
to: 'sibling_id',
},
});
}
}
Model can be a model object, or an absolute path to a model class. It can also be a relative path if you set the basePath of all models using Model.setBasePath(path)
InputError Class
import {InputError} from 'boldr-orm';
async function saveStore(store) {
if (!store.name) {
throw new InputError({
name: 'Name is required',
});
}
}
// Alternatively instead of importing InputError you can simply throw Model.Error
async function saveStore(store) {
if (!store.name) {
throw new Store.Error({
name: 'Name is required',
});
}
}