- Get Started
- Product
- Resources
- Tools & SDKs
- Framework
- Reference
- Get Started
- Product
- Resources
- Tools & SDKs
- Framework
- Reference
4.5.6. Data Model Relationships
In this chapter, you’ll learn how to define relationships between data models in your module.
What is a Relationship Property?#
A relationship property defines an association in the database between two models. It's created using methods on the models
utility, such as hasOne
or belongsTo
.
When you generate a migration for these data models, the migrations include foreign key columns or pivot tables, based on the relationship's type.
One-to-One Relationship#
A one-to-one relationship indicates that one record of a data model belongs to or is associated with another.
To define a one-to-one relationship, create relationship properties in the data models using the following methods:
hasOne
: indicates that the model has one records of the specified model.belongsTo
: indicates that the model belongs to one record of the specified model.
For example:
In the example above, a user has one email, and an email belongs to one user.
The hasOne
and belongsTo
methods accept a function as a first parameter. The function returns the associated data model.
The belongsTo
method also requires passing as a second parameter an object with the property mappedBy
. Its value is the name of the relationship property in the other data model.
Optional Relationship#
To make the relationship optional on the hasOne
or belongsTo
side, use the nullable
method on either properties as explained in this chapter.
One-to-One Relationship in the Database#
When you generate the migrations of data models that have a one-to-one relationship, the migration adds to the table of the data model that has the belongsTo
property:
- A column of the format
{relation_name}_id
to store the ID of the record of the related data model. For example, theemail
table will have auser_id
column. - A foreign key on the
{relation_name}_id
column to the table of the related data model.
One-to-Many Relationship#
A one-to-many relationship indicates that one record of a data model has many records of another data model.
To define a one-to-many relationship, create relationship properties in the data models using the following methods:
hasMany
: indicates that the model has more than one records of the specified model.belongsTo
: indicates that the model belongs to one record of the specified model.
For example:
1import { model } from "@medusajs/framework/utils"2 3const Store = model.define("store", {4 id: model.id().primaryKey(),5 products: model.hasMany(() => Product),6})7 8const Product = model.define("product", {9 id: model.id().primaryKey(),10 store: model.belongsTo(() => Store, {11 mappedBy: "products",12 }),13})
In this example, a store has many products, but a product belongs to one store.
Optional Relationship#
To make the relationship optional on the belongsTo
side, use the nullable
method on the property as explained in this chapter.
One-to-Many Relationship in the Database#
When you generate the migrations of data models that have a one-to-many relationship, the migration adds to the table of the data model that has the belongsTo
property:
- A column of the format
{relation_name}_id
to store the ID of the record of the related data model. For example, theproduct
table will have astore_id
column. - A foreign key on the
{relation_name}_id
column to the table of the related data model.
Many-to-Many Relationship#
A many-to-many relationship indicates that many records of a data model can be associated to many records of another data model.
To define a many-to-many relationship, create relationship properties in the data models using the manyToMany
method.
For example:
1import { model } from "@medusajs/framework/utils"2 3const Order = model.define("order", {4 id: model.id().primaryKey(),5 products: model.manyToMany(() => Product, {6 mappedBy: "orders",7 }),8})9 10const Product = model.define("product", {11 id: model.id().primaryKey(),12 orders: model.manyToMany(() => Order, {13 mappedBy: "products",14 }),15})
At least one side of the many-to-many relationship must have the mappedBy
property set in the second object parameter of the manyToMany
object. Its value is the name of the relationship property in the other data model.
In this example, an order is associated with many products, and a product is associated with many orders.
Many-to-Many Relationship in the Database#
When you generate the migrations of data models that have a many-to-many relationship, the migration adds a new pivot table.
The pivot table has a column with the name {data_model}_id
for each of the data model's tables. It also has foreign keys on each of these columns to their respective tables.
Set Relationship Name in the Other Model#
The relationship property methods accept as a second parameter an object of options. The mappedBy
property defines the name of the relationship in the other data model.
This is useful if the relationship property’s name is different than that of the associated data model.
As seen in previous examples, the mappedBy
option is required for the belongsTo
method.
For example:
1import { model } from "@medusajs/framework/utils"2 3const User = model.define("user", {4 id: model.id().primaryKey(),5 email: model.hasOne(() => Email, {6 mappedBy: "owner",7 }),8})9 10const Email = model.define("email", {11 id: model.id().primaryKey(),12 owner: model.belongsTo(() => User, {13 mappedBy: "email",14 }),15})
In this example, you specify in the User
data model’s relationship property that the name of the relationship in the Email
data model is owner
.
Cascades#
When an operation is performed on a data model, such as record deletion, the relationship cascade specifies what related data model records should be affected by it.
For example, if a store is deleted, its products should also be deleted.
The cascades
method used on a data model configures which child records an operation is cascaded to.
For example:
1import { model } from "@medusajs/framework/utils"2 3const Store = model.define("store", {4 id: model.id().primaryKey(),5 products: model.hasMany(() => Product),6})7.cascades({8 delete: ["products"],9})10 11const Product = model.define("product", {12 id: model.id().primaryKey(),13 store: model.belongsTo(() => Store, {14 mappedBy: "products",15 }),16})
The cascades
method accepts an object. Its key is the operation’s name, such as delete
. The value is an array of relationship property names that the operation is cascaded to.
In the example above, when a store is deleted, its associated products are also deleted.