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.
static tableName = 'Article'; ... ...
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';
// Enable soft delete static softDelete = true; // soft deletePost;// restore an archived rowPost;// get all non-archived postsPost;// get all posts (including archived / "soft deleted" ones)Post;// retrieve only archived postsPost;// completely remove a post from the databasePost;
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 and (`posts` = 100 or `user` = 'admin')
whereByAnd(obj)
creates an or (and - and - and) condition
q// 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 equivalentPerson;Person;Person; // both are equivalentPerson;Person;
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
}
static this; this; this; this;
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
; { if !storename throw name: 'Name is required' ; } // Alternatively instead of importing InputError you can simply throw Model.Error { if !storename throw name: 'Name is required' ; }