# Relations
We already went through how to create relationships (aka. relations, associations) in the models section's examples but here's a list of all the available relation types in a nicely searchable place. See this API doc section for full documentation of the relation mapping parameters.
Relationships are a very basic concept in relational databases and if you aren't familiar with it, you should spend some time googling it first. Basically there are three ways to create a relationship between two tables A
and B
:
Table
A
has a column that holds tableB
's id. This relationship is called aBelongsToOneRelation
in objection. We can say thatA
belongs to oneB
.Table
B
has a column that holds tableA
's id. This relationship is called aHasManyRelation
in objection. We can say thatA
has manyB
's.Table
C
has columns for bothA
andB
tables' identifiers. This relationship is calledManyToManyRelation
in objection. Each row inC
joins oneA
with oneB
. Therefore anA
row can be related to multipleB
rows and aB
row can be related to multipleA
rows through tableC
.
While relations are usually created between the primary key of one table and a foreign key reference of another table, objection has no such limitations. You can create relationship using any two columns (or any sets of columns). You can even create relation using values nested deep inside json columns.
If you've used other ORMs you may notice that objection's relationMappings are pretty verbose. There are couple of reasons for that:
For a new user, this style underlines what is happening, and which columns and tables are involved.
You only need to define relations once. Writing a couple of lines more for clarity shouldn't impact your productivity.
# Examples
Vocabulary for the relation descriptions:
- source model: The model for which you are writing the
relationMapping
for. - related model: The model at the other end of the relation.
BelongsToOneRelation
: Use this relation when the source model has the foreign key
class Animal extends Model {
static tableName = 'animals';
static relationMappings = {
owner: {
relation: Model.BelongsToOneRelation,
modelClass: Person,
join: {
from: 'animals.ownerId',
to: 'persons.id'
}
}
};
}
HasManyRelation
: Use this relation when the related model has the foreign key
class Person extends Model {
static tableName = 'persons';
static relationMappings = {
animals: {
relation: Model.HasManyRelation,
modelClass: Animal,
join: {
from: 'persons.id',
to: 'animals.ownerId'
}
}
};
}
HasOneRelation
: Just like HasManyRelation
but for one related row
class Person extends Model {
static tableName = 'persons';
static relationMappings = {
animal: {
relation: Model.HasOneRelation,
modelClass: Animal,
join: {
from: 'persons.id',
to: 'animals.ownerId'
}
}
};
}
ManyToManyRelation
: Use this relation when the model is related to a list of other models through a join table
class Person extends Model {
static tableName = 'persons';
static relationMappings = {
movies: {
relation: Model.ManyToManyRelation,
modelClass: Movie,
join: {
from: 'persons.id',
through: {
// persons_movies is the join table.
from: 'persons_movies.personId',
to: 'persons_movies.movieId'
},
to: 'movies.id'
}
}
};
}
HasOneThroughRelation
: Use this relation when the model is related to a single model through a join table
class Person extends Model {
static tableName = 'persons';
static relationMappings = {
movie: {
relation: Model.HasOneThroughRelation,
modelClass: Movie,
join: {
from: 'persons.id',
through: {
// persons_movies is the join table.
from: 'persons_movies.personId',
to: 'persons_movies.movieId'
},
to: 'movies.id'
}
}
};
}
# Require loops (non ECMAScript modules only)
Require loops (circular dependencies, circular requires) are a very common problem when defining relations. Whenever a module A
imports module B
that immediately (synchronously) imports module A
, you create a require loop that node.js or objection cannot solve automatically. A require loop usually leads to the other imported value to be an empty object which causes all kinds of problems. Objection attempts to detect these situations and mention the words require loop
in the thrown error. Objection offers multiple solutions to this problem. See the circular dependency solutions examples in this section. In addition to objection's solutions, you can always organize your code so that such loops are not created.
If you are using ECMAScript modules (opens new window), circular imports are not a problem. You can just do:
import Animal from "./Animal.js";
class Person extends Model {
static get tableName() {
return "persons";
}
static get relationMappings() {
return {
pets: {
relation: Model.HasManyRelation,
modelClass: Animal,
join: {
from: "persons.id",
to: "animals.ownerId",
},
},
};
}
}
However if you are not using ECMAScript modules, solutions to require loops are:
class Person extends Model {
static get tableName() {
return 'persons';
}
static get relationMappings() {
// Solution 1:
//
// relationMappings getter is accessed lazily when you execute
// your first query that needs it. Therefore if you `require`
// your models inside the getter, you don't end up with a require loop.
// Note that only one end of the relation needs to be required like
// this, not both. `relationMappings` can also be a method or
// a thunk if you prefer those instead of getters.
const Animal = require('./Animal');
return {
pets: {
relation: Model.HasManyRelation,
modelClass: Animal,
join: {
from: 'persons.id',
to: 'animals.ownerId'
}
},
movies: {
relation: Model.ManyToManyRelation,
// Solution 2:
//
// Absolute file path to a module that exports the model class.
// This is similar to solution 1, but objection calls `require`
// under the hood. The downside here is that you need to give
// an absolute file path because of the way `require` works.
modelClass: path.join(__dirname, 'Movie'),
join: {
from: 'persons.id',
through: {
// persons_movies is the join table.
from: 'persons_movies.personId',
to: 'persons_movies.movieId'
},
to: 'movies.id'
}
},
movies: {
relation: Model.ManyToManyRelation,
// Solution 3:
//
// Use only a module name and define a `modelPaths` property
// for your model (or a superclass of your model). Search for
// `modelPaths` from the docs for more info.
modelClass: 'Movie',
join: {
from: 'persons.id',
through: {
from: 'persons_movies.personId',
to: 'persons_movies.movieId'
},
to: 'movies.id'
}
}
};
}
}
← Models Query examples →