Grind’s ORM is powered by Objection.js which provides full ES6/7 class-based Models.
This document only focuses on additional functionality Grind ORM provides on top of Objection.js, for full documentation on how Objection.js works, head over to vincit.github.io/objection.js.
First, add the grind-orm
package via your preferred package manager:
yarn add grind-orm
Next, you’ll need to add OrmProvider
to your app providers in app/Boostrap.js
:
const app =appproviders
The fastest way to create a new model is by using the model generator via bin/cli make:model
.
You can invoke make:model
with a few different arguments:
bin/cli make:model UserModel
will createapp/Models/UserModel.js
, but will not infer a table name.bin/cli make:model --table=users
will also createapp/Models/UserModel.js
and will set the table name for you.- You can also pass both a class name and a command name at the same time if your class name differs from the table name.
Once you’ve triggered make:model
, a model is generated for you that looks like this:
static tableName = 'users'static descriptiveName = 'user'static jsonSchema =type: 'object'required: 'name'properties:id: type: 'integer'name: type: 'string' maxLength: 255created_at: type: 'string' format: 'date-time'updated_at: type: 'string' format: 'date-time'static {return// children: this.hasMany(ChildModel)}
This is the name of the table this model represents and tells the query builder which table to query on.
This is used by the describe
function throughout the framework to describe what this model represents, most prevalently in error messages. If descriptiveName
is not provided, the Model class will generate one based on the tableName
.
This is used by Objection.js to validate data going into your database. It’s not required but highly recommended to ensure your data looks like it’s expected to.
The
jsonSchema
properties are not tied directly to the database schema. They’re solely intended for validation and will not be used to generate migrations. You are still responsible for building out the backing database schema the model represents.
This function is called by Grind’s Model to populate the relationMappings
property in Objection.js.
Grind’s Model offers an alternative way to establish relationships when compared to Objection.js’s relationMappings
property, which can get fairly verbose and repetitive.
Grind uses a buildRelations
function to return an object of relations that are then used to populate the relationMappings
class property.
Grind’s Model offers several functions to help build relations:
hasOne
establishes a one to one relationship where the target model has a property that references a property of the local model.
modelClass
— Target model you’re establishing a relationship withforeignKey
— The property on the target model that references the local model. If not provided, Model will generate a foreign key based on the local model’s name,UserModel
becomesuser_id
.localKey
— The property thatforeignKey
references on the local model. If no value is provided, it uses the local model’sidColumn
property, which defaults toid
.
If UserModel
calls this.hasOne(AvatarModel)
it establishes that AvatarModel
has a user_id
property that references id
on UserModel
.
hasMany
establishes a one to many relationship where the target model has a property that references a property of the local model.
modelClass
— Target model you’re establishing a relationship withforeignKey
— The property on the target model that references the local model. If not provided, Model will generate a foreign key based on the local model’s name,UserModel
becomesuser_id
.localKey
— The property thatforeignKey
references on the local model. If no value is provided, it uses the local model’sidColumn
property, which defaults toid
.
If UserModel
calls this.hasMany(PostModel)
it establishes that PostModel
has a user_id
property that references id
on UserModel
.
belongsTo
is the inverse of hasOne
/hasMany
and establishes a one to one relationship where the local model has a property that references a property of the target model.
modelClass
— Target model you’re establishing a relationship withforeignKey
— The property on the target model that references the local model. If not provided, the Model will generate a foreign key based on the local model’s name,UserModel
becomesuser_id
.otherKey
— The property thatforeignKey
references on the target class. If no value is provided, it uses the target modelsidColumn
property, which defaults toid
.
If PostModel
calls this.belongsTo(UserModel)
it establishes that PostModel
has a user_id
property that references id
on UserModel
.
belongsToMany
establishes a many to many relationship using a join table.
modelClass
— Target model you’re establishing a relationship withtableName
— Name of the join table to connect the two models. If not provided, the Model will generate a table name based on the names of the tables on each end, sorted ascendingly and joined with an underscore. If you’re establishing a relationship betweenUserModel
andBlogModel
the default join table name will beblogs_users
.foreignKey
— The column on the join table that references the target model. If not provided, the Model will generate a foreign key based on the target model’s name,BlogModel
becomesblog_id
.otherKey
— The column on the join table that references the local model. If not provided, the Model will generate a foreign key based on the local model’s name,UserModel
becomesuser_id
.
If UserModel
calls this.belongsToMany(BlogModel)
it established that UserModel
is connected to BlogModel
through a joint able called blogs_users
with blogs_users.user_id
referencing UserModel.idColumn
and blogs_users.blog_id
referencing BlogModel.idColumn
.
If wanted to establish a followers
relationship, you could define it as:
this
This would establish that one UserModel
is related to another UserModel
through a followers
join table.
Putting it all together, here’s an example of creating a few relationships using Grind Model’s buildRelations
:
static {returnavatar: thisposts: thisblogs: thisfollowers: thisfollowing: this}
Grind ORM extends Objection.js’s already powerful QueryBuider to provide some additional functionality.
The paginate
method provides a way to return paginated results with a object result object that includes the total number of items, number of pages, the current page, and more.
req
— The original http request object — this is used to automatically detect the current page.perPage
— The number of items to include in a single pageoptions.param
— If provided,paginate
will look for the current page number in a URL param instead of the query string. This value will override any providedoptions.query
value.options.query
— By default,paginate
looks for apage
field in the query string, this option can be used to tell it to look for a different field.
totalPages
— The total number of pages.perPage
— The number of items included in each page.page
— The current page number.start
— The start range for this page.end
— The end range for this page.total
— The total number of items for this page.results
— The items in this page.
The orFail
method will throw a ModelNotFoundError
if the result set is empty.
const user = UserModel
By tacking on orFail()
to the query, your code can now be confident the user
var is populated, saving you the need to check and throw errors. orFail()
will automatically throw ModelNotFoundError
— which extends NotFoundError, resulting in a 404 being displayed to the end user.
The withoutEager
method will clear any previously added eager filter as well as ignore any Model level eager filters that may be defined.
const user = UserModel
Objection.js has an amazing way to do eager loading, so Grind Model builds on top of it to add a few additional niceties.
Grind Model provides a way to define eager loading on a Model level that will ensure whenever your model is used, it will eager load those relationships.
static eager = '[owner,posts(latest)]'static eagerFilters =builder
The eager
class property lets you define the relation expression to load every time the model is queried.
You an also define filters for eager
by setting them on the (optional) eagerFilter
class property.
Grind Model enables you to define global filters to use in any eager expression by calling QueryBuilder.registerFilter()
.
The recommended way to load filters is via a provider:
{ModelQueryBuilder}
Now you can just use the active
filter in any eager expression without having to declare it each time.