Relations Between Entities 🚀
New Feature
Relations are a new feature introduced in version 0.23.
Understanding Entity Relations in Remult
In Remult, entity relations play a useful role in modeling and navigating the complex relationships that exist within your data. To illustrate this concept, we will use two primary entities: Customer
and Order
. These entities will serve as the foundation for discussing various types of relations and how to define and work with them .
To experiment with these entities online, you can access the following CodeSandbox link, which is preconfigured with these two entities and a postgres database:
CodeSandbox - Remult Entity Relations Example
Feel free to explore and experiment with the provided entities and their relations in the CodeSandbox environment.
Customer Entity
// customer.ts
import { Entity, Fields } from 'remult'
@Entity('customers')
export class Customer {
@Fields.cuid()
id = ''
@Fields.string()
name = ''
@Fields.string()
city = ''
}
The Customer
entity represents individuals or organizations with attributes such as an ID, name, and city. Each customer can be uniquely identified by their id
.
Order Entity
// order.ts
import { Entity, Fields } from 'remult'
@Entity('orders')
export class Order {
@Fields.cuid()
id = ''
@Fields.string()
customer = ''
@Fields.number()
amount = 0
}
The Order
entity represents transactions or purchases made by customers. Each order is associated with a customer
, representing the customer who placed the order, and has an amount
attribute indicating the total purchase amount.
Throughout the following discussion, we will explore how to define and use relations between these entities, enabling you to create sophisticated data models and efficiently query and manipulate data using Remult. Whether you are dealing with one-to-one, one-to-many, or many-to-many relationships, understanding entity relations is essential for building robust and feature-rich applications with Remult.
Simple Many-to-One
In Remult, many-to-one relations allow you to establish connections between entities, where multiple records of one entity are associated with a single record in another entity. Let's delve into a common use case of a many-to-one relation, specifically the relationship between the Order
and Customer
entities.
Defining the Relation
To establish a many-to-one relation from the Order
entity to the Customer
entity, you can use the @Relations.toOne()
decorator in your entity definition:
// order.ts
import { Entity, Fields, Relations } from 'remult'
import { Customer } from '../customer.js'
@Entity('orders')
export class Order {
@Fields.cuid()
id = ''
@Fields.string()
customer = ''
@Relations.toOne(() => Customer)
customer?: Customer
@Fields.number()
amount = 0
}
In this example, each Order
is associated with a single Customer
. The customer
property in the Order
entity represents this relationship.
Fetching Relational Data
When querying data that involves a many-to-one relation, you can use the include
option to specify which related entity you want to include in the result set. In this case, we want to include the associated Customer
when querying Order
records.
Here's how you can include the relation in a query using Remult:
const orderRepo = remult.repo(Order)
const orders = await orderRepo.find({
include: {
customer: true,
},
})
Resulting Data Structure
The result of the query will contain the related Customer
information within each Order
record, creating a nested structure.
Here's an example result of running JSON.stringify
on the orders
array:
[
{
"id": "adjkzsio3efees8ew0wnsqma",
"customer": {
"id": "m4ozs74onwwroav3o1xs1qi8",
"name": "Larkin - Fadel",
"city": "London"
},
"amount": 90
},
{
"id": "gefhsed1clknmogcgiigo9jo",
"customer": {
"id": "m4ozs74onwwroav3o1xs1qi8",
"name": "Larkin - Fadel",
"city": "London"
},
"amount": 3
}
]
As shown in the result, each Order
object contains a nested customer
object, which holds the details of the associated customer, including their id
, name
, and city
. This structured data allows you to work seamlessly with the many-to-one relationship between Order
and Customer
entities .
Querying a Single Item
To retrieve a single Order
item along with its associated Customer
, you can use the findFirst
method provided by your repository (orderRepo
in this case). Here's an example of how to perform this query:
const singleOrder = await orderRepo.findFirst(
{
id: 'adjkzsio3efees8ew0wnsqma',
},
{
include: {
customer: true,
},
},
)
Relation Loading
In Remult, by default, a relation is not loaded unless explicitly specified in the include
statement of a query. This behavior ensures that you only load the related data you require for a specific task, optimizing performance and minimizing unnecessary data retrieval.
Here's an example:
const orderRepo = remult.repo(Order)
// Query without including the 'customer' relation
const ordersWithoutCustomer = await orderRepo.find({})
In the above query, the customer
relation will not be loaded and have the value of undefined
because it is not specified in the include
statement.
Overriding Default Behavior with defaultIncluded
Sometimes, you may have scenarios where you want a relation to be included by default in most queries, but you also want the flexibility to exclude it in specific cases. Remult allows you to control this behavior by using the defaultIncluded
setting in the relation definition.
@Relations.toOne(() => Customer, {
defaultIncluded: true,
})
customer = "";
In this example, we set defaultIncluded
to true
for the customer
relation in the Order
entity. This means that, by default, the customer
relation will be loaded in most queries unless explicitly excluded.
Example: Excluding customer
Relation in a Specific Query
const orders = await orderRepo.find({
include: {
customer: false,
},
})
In this query, we override the default behavior by explicitly setting customer: false
in the include
statement. This instructs Remult not to load the customer
relation for this specific query, even though it is set to be included by default.
By combining the default behavior with the ability to override it in specific queries, Remult provides you with fine-grained control over relation loading, ensuring that you can optimize data retrieval based on your application's requirements and performance considerations.
Advanced Many-to-One
In certain scenarios, you may require more granular control over the behavior of relations and want to access specific related data without loading the entire related entity. Remult provides advanced configuration options to meet these requirements. Let's explore how to achieve this level of control through advanced relation configurations.
Custom Relation Field
In Remult, you can define custom relation fields that allow you to access the id
without loading the entire related entity. To define a custom relation field, follow these steps:
Step 1: Define a Custom Field in the Entity
In your entity definition, define a custom field that will hold the identifier or key of the related entity. This field serves as a reference to the related entity without loading the entity itself.
@Entity("orders")
export class Order {
@Fields.cuid()
id = "";
@Fields.string()
customerId = ""; // Custom field to hold the related entity's identifier
@Relations.toOne<Order, Customer>(() => Customer, "customerId")
@Relations.toOne(() => Customer)
customer?: Customer;
@Fields.number()
amount = 0;
}
In this example, we define a custom field called customerId
, which stores the identifier of the related Customer
entity.
Step 2: Define the Relation Using toOne
Use the @Relations.toOne
decorator to define the relation, specifying the types for the fromEntity
and toEntity
in the generic parameters. Additionally, provide the name of the custom field (in this case, "customerId"
) as the third argument.
@Entity('orders')
export class Order {
@Fields.cuid()
id = ''
@Fields.string()
customerId = '' // Custom field to hold the related entity's identifier
@Relations.toOne<Order, Customer>(() => Customer, 'customerId')
customer = ''
@Fields.number()
amount = 0
}
This configuration establishes a relation between Order
and Customer
using the customerId
field as the reference.
Migrating from a Simple toOne
Relation to a Custom Field Relation with Existing Data
When transitioning from a simple toOne
relation to a custom field relation in Remult and you already have existing data, it's important to ensure a smooth migration. In this scenario, you need to make sure that the newly introduced custom field (customerId
in this case) can access the existing data in your database. This is accomplished using the dbName
option. Here's how to perform this migration:
1. Understand the Existing Data Structure
Before making any changes, it's crucial to understand the structure of your existing data. In the case of a simple toOne
relation, there may be rows in your database where a field (e.g., customer
) holds the identifier of the related entity.
2. Define the Custom Field with dbName
When defining the custom field in your entity, use the dbName
option to specify the name of the database column where the related entity's identifier is stored. This ensures that the custom field (customerId
in this example) correctly accesses the existing data in your database.
@Entity('orders')
export class Order {
@Fields.cuid()
id = ''
@Fields.string({ dbName: 'customer' }) // Use dbName to match existing data
customerId = ''
@Relations.toOne(() => Customer, 'customerId')
customer?: Customer
@Fields.number()
amount = 0
}
In this example, we use the dbName
option to specify that the customerId
field corresponds to the customer
column in the database. This mapping ensures that the custom field can access the existing data that uses the customer
column for the related entity's identifier.
Using the field
Option for Custom Relation Configuration
When you require additional customization for a relation field in Remult, you can utilize the field option to specify additional options for the related field.
@Relations.toOne<Order, Customer>(() => Customer, {
field: "customerId",
caption: "The Customer",
})
In this example, we use the field
option to define a custom relation between the Order
and Customer
entities. Here are some key points to understand about using the field
option:
Custom Relation Field: The
field
option allows you to specify a custom field name (e.g.,"customerId"
) that represents the relationship between entities. This field can be used to access related data without loading the entire related entity.Additional Configuration: In addition to specifying the
field
, you can include other options as well. In this example, we set thecaption
option to provide a descriptive caption for the relation field.
Using the field
option provides you with granular control over how the relation field is configured and accessed . You can customize various aspects of the relation to meet your specific requirements, enhance documentation, and improve the overall usability of your codebase.
Relation Based on Multiple Fields
In some scenarios, establishing a relation between entities requires considering multiple fields to ensure the correct association. Remult provides the flexibility to define relations based on multiple fields using the fields
option. Here's how to create a relation based on multiple fields in Remult:
Defining Entities
Let's consider a scenario where both Order
and Customer
entities belong to specific branches, and we need also the branchId
fields to ensure the correct association. First, define your entities with the relevant fields:
@Entity('customers')
export class Customer {
@Fields.cuid()
id = ''
@Fields.number()
branchId = 0
@Fields.string()
name = ''
@Fields.string()
city = ''
}
@Entity('orders')
export class Order {
@Fields.cuid()
id = ''
@Fields.number()
branchId = 0
@Fields.string({ dbName: 'customer' })
customerId = ''
@Relations.toOne<Order, Customer>(() => Customer, {
fields: {//[!code ++]
branchId: 'branchId', // Field from Customer entity : Field from Order
id: 'customerId',
},
})
customer?: Customer
@Fields.number()
amount = 0
}
In this example, we have two entities: Customer
and Order
. Both entities have a branchId
field that represents the branch they belong to. To create a relation based on these fields, we specify the fields
option in the relation configuration.
Using the fields
Option
In the @Relations.toOne
decorator, use the fields
option to specify the mapping between fields in the related entity (Customer
) and your entity (Order
). Each entry in the fields
object corresponds to a field in the related entity and maps it to a field in your entity.
@Relations.toOne<Order, Customer>(() => Customer, {
fields: {
branchId: 'branchId', // Field from Customer entity : Field from Order
id: 'customerId',
},
})
customer?: Customer;
In this configuration:
branchId
from theCustomer
entity is mapped tobranchId
in theOrder
entity.id
from theOrder
entity is mapped tocustomerId
in theCustomer
entity.
This ensures that the relation between Order
and Customer
is based on both the branchId
and customerId
fields, providing a comprehensive association between the entities.
By utilizing the fields
option, you can create relations that consider multiple fields, ensuring accurate and meaningful associations between your entities in Remult.
One-to-Many
In Remult, you can easily define a toMany
relation to retrieve multiple related records. Let's consider a scenario where you want to retrieve a list of orders for each customer. We'll start with the basic toOne
relation example and then add a toMany
relation to achieve this:
Basic toOne
Relation Example
First, let's define the Customer
and Order
entities with a basic toOne
relation:
@Entity("customers")
export class Customer {
@Fields.cuid()
id = "";
@Fields.string()
name = "";
@Fields.string()
city = "";
}
@Entity("orders")
export class Order {
@Fields.cuid()
id = "";
@Relations.toOne(() => Customer)
customer?: Customer;
@Fields.number()
amount = 0;
}
In this initial setup:
- The
Order
entity has a propertycustomer
, which is decorated with@Relations.toOne(() => Customer)
. This establishes a relation between an order and its associated customer.
Adding a toMany
Relation
Now, let's enhance this setup to include a toMany
relation that allows you to retrieve a customer's orders:
@Entity('customers')
export class Customer {
@Fields.cuid()
id = ''
@Fields.string()
name = ''
@Fields.string()
city = ''
@Relations.toMany(() => Order)
orders?: Order[]
}
In this updated configuration:
- The
Customer
entity has a propertyorders
, which is decorated with@Relations.toMany(() => Order)
. This indicates that a customer can have multiple orders.
With this setup, you can use the orders
property of a Customer
entity to retrieve all the orders associated with that customer. This provides a convenient way to access and work with a customer's orders.
By defining a toMany
relation, you can easily retrieve and manage multiple related records, such as a customer's orders.
Fetching Relational Data
To retrieve customers along with their associated order in Remult, you can use the include
option in your query. Let's see how to fetch customers with their orders using the include
option:
const customerRepo = remult.repo(Customer)
const customers = await customerRepo.find({
include: {
orders: true,
},
})
In this code snippet:
We first obtain a repository for the
Customer
entity usingremult.repo(Customer)
.Next, we use the
find
method to query theCustomer
entity. Within the query options, we specify theinclude
option to indicate that we want to include related records.Inside the
include
option, we specifyorders: true
, indicating that we want to fetch the associated orders for each customer.
As a result, the customers
variable will contain an array of customer records, with each customer's associated orders included. This allows you to easily access and work with both customer and order data.
Resulting Data Structure
When you fetch customers along with their associated orders using the include
option in Remult, the result will be an array that includes both customer and order data.
Here's an example result of running JSON.stringify
on the customers
array:
[
{
"id": "ik68p3oxqg1ygdffpryqwkpw",
"name": "Fay, Ebert and Sporer",
"city": "London",
"orders": [
{
"id": "m7m3xqyx4kwjaqcd0cu33q8g",
"amount": 15
},
{
"id": "rbkcrz6nc45zn4xfxmjise21",
"amount": 10
}
]
}
]
In this example:
Each customer is represented as an object with properties such as
id
,name
, andcity
.The
orders
property within each customer object contains an array of associated order records.Each order record within the
orders
array includes properties likeid
andamount
.
This structured result allows you to easily navigate and manipulate the data . You can access customer information as well as the details of their associated orders, making it convenient to work with related records in your application's logic and UI.
Specifying Reference Fields
In Remult, you can specify a field or fields for toMany
relations to have more control over how related records are retrieved. This can be useful when you want to customize the behavior of the relation. Here's how you can specify a field or fields for toMany
relations:
Specifying a Single Field
To specify a single field for a toMany
relation, you can use the field
option. This option allows you to define the field in your entity that establishes the relation. For example:
@Relations.toMany<Customer, Order>(() => Order, {
field: "customer",
})
In this case, the field
option is set to "customer"
, indicating that the customer
field in the Order
entity establishes the relation between customers and their orders.
Specifying Multiple Fields
In some cases, you may need to specify multiple fields to establish a toMany
relation. To do this, you can use the fields
option, which allows you to define a mapping of fields between entities. Here's an example:
@Relations.toMany<Customer, Order>(() => Order, {
fields: {
branchId: "branchId",
customerId: "id",
},
})
In this example, the fields
option is used to specify that the branchId
field in the Order
entity corresponds to the branchId
field in the Customer
entity, and the customerId
field in the Order
entity corresponds to the id
field in the Customer
entity.
By specifying fields in this manner, you have fine-grained control over how the relation is established and how related records are retrieved. This allows you to tailor the behavior of toMany
relations to your specific use case and data model.
Customizing a toMany
Relation
In Remult, you can exercise precise control over a toMany
relation by utilizing the findOptions
option. This option allows you to define specific criteria and behaviors for retrieving related records. Here's how you can use findOptions
to fine-tune a toMany
relation:
@Relations.toMany<Customer, Order>(() => Order, {
fields: {
branchId: "branchId",
customerId: "id",
},
findOptions: {
limit: 5,
orderBy: {
amount: "desc",
},
where: {
amount: { $gt: 10 },
},
},
})
In this example, we've specified the following findOptions
:
limit: 5
: Limits the number of related records to 5. Only the first 5 related records will be included.orderBy: { amount: "desc" }
: Orders the related records by theamount
field in descending order. This means that records with higheramount
values will appear first in the result.where: { amount: { $gt: 10 } }
: Applies a filter to include only related records where theamount
is greater than 10. This filters out records with anamount
of 10 or lower.
By using findOptions
in this manner, you gain precise control over how related records are retrieved and included in your query results. This flexibility allows you to tailor the behavior of the toMany
relation to suit your specific application requirements and use cases.
Fine-Tuning a toMany
Relation with include
In Remult, you can exercise even more control over a toMany
relation by using the include
option within your queries. This option allows you to further customize the behavior of the relation for a specific query. Here's how you can use include
to fine-tune a toMany
relation:
const orders = await customerRepo.find({
include: {
orders: {
limit: 10,
where: {
completed: true,
},
},
},
})
In this code snippet:
We use the
include
option within our query to specify that we want to include the relatedorders
for each customer.Inside the
include
block, we can provide additional options to control the behavior of this specific inclusion. For example:limit: 10
limits the number of related orders to 10 per customer. This will override thelimit
set in the original relation.where: { completed: true }
filters the included orders to only include those that have been marked as completed.
The where
option specified within include
will be combined with the where
conditions defined in the findOptions
of the relation using an "and" relationship. This means that both sets of conditions must be satisfied for related records to be included.
Using include
in this way allows you to fine-tune the behavior of your toMany
relation to meet the specific requirements of each query, making Remult a powerful tool for building flexible and customized data retrieval logic in your application.
Repository relations
In Remult, managing relationships between entities is a crucial aspect of working with your data. When dealing with a toMany
relationship, Remult provides you with powerful tools through the repository's relations
property to handle related rows efficiently, whether you want to retrieve them or insert new related records.
Inserting Related Records
Consider a scenario where you have a Customer
entity with a toMany
relationship to Order
entities. You can create a new customer and insert related orders in a straightforward manner:
const customer = await customerRepo.insert({ name: 'Abshire Inc' })
await customerRepo.relations(customer).orders.insert([
{
amount: 5,
},
{
amount: 7,
},
])
In this example, you first create a new Customer
entity with the name "Abshire Inc." Then, using the relations
method, you access the related orders
. By calling the insert
method on the orders
relation, you can add new order records. Remult automatically sets the customer
field for these orders based on the specific customer associated with the relations
call.
Loading Unfetched Relations
Another powerful use of the repository
methods is to load related records that were not initially retrieved. Let's say you have found a specific customer and want to access their related orders:
const customerRepo = remult.repo(Customer)
const customer = await customerRepo.findFirst({ name: 'Abshire Inc' })
const orders = await customerRepo.relations(customer).orders.find()
Here, you first search for a customer with the name "Abshire Inc." After locating the customer, you can use the relations
method again to access their related orders. By calling the find
method on the orders
relation, you retrieve all related order records associated with the customer.
Contextual Repository: Tailored Operations for Related Data
The relations
method serves as a specialized repository, tightly associated with the particular customer you supply to it. This dedicated repository offers a tailored context for performing operations related to the specific customer's connection to orders. It enables you to seamlessly find related records, insert new ones, calculate counts, and perform other relevant actions within the precise scope of that customer's relationship with orders. This versatile capability streamlines the management of intricate relationships in your application, ensuring your data interactions remain organized and efficient.
Remult's repository methods empower you to seamlessly manage and interact with related data, making it easier to work with complex data structures and relationships in your applications. Whether you need to insert related records or load unfetched relations, these tools provide the flexibility and control you need to handle your data efficiently.
Certainly, here's an extension of the "Loading Unfetched Relations" section that covers the topic of fetching unloaded toOne
relations using the findOne
function:
Fetching Unloaded toOne
Relations with findOne
In addition to loading unfetched toMany
relations, Remult offers a convenient way to retrieve toOne
relations that were not initially loaded. This capability is especially useful when dealing with many-to-one relationships.
Consider the following example, where we have a many-to-one relation between orders and customers. We want to fetch the customer related to a specific order, even if we didn't load it initially:
const orderRepo = remult.repo(Order)
const order = await orderRepo.findFirst({ id: 'm7m3xqyx4kwjaqcd0cu33q8g' })
const customer = await orderRepo.relations(order).customer.findOne()
In this code snippet:
- We first obtain the order using the
findFirst
function, providing the order's unique identifier. - Next, we use the
relations
method to access the repository's relations and then chain thecustomer
relation using dot notation. - Finally, we call
findOne()
on thecustomer
relation to efficiently retrieve the related customer information.
This approach allows you to access and load related data on-demand, providing flexibility and control over your data retrieval process. Whether you're working with loaded or unloaded relations, Remult's intuitive functions give you the power to seamlessly access the data you need.
You can seamlessly incorporate this extension into the "Loading Unfetched Relations" section of your documentation to provide a comprehensive overview of working with both toMany
and toOne
relations.
Accessing Relations with activeRecord
If you're following the activeRecord
pattern and your entity inherits from EntityBase
or IdEntity
, you can access relations directly from the entity instance. This approach offers a convenient and straightforward way to work with relations.
Inserting Related Records
You can insert related records directly from the entity instance. For example, consider a scenario where you have a Customer
entity and a toMany
relation with Order
entities. Here's how you can insert related orders for a specific customer:
const customer = await customerRepo.insert({ name: 'Abshire Inc' })
await customer._.relations.orders.insert([
{
amount: 5,
},
{
amount: 7,
},
])
In this code:
- We create a new
Customer
instance usingcustomerRepo.insert()
and set its properties. - Using
customer._.relations.orders
, we access theorders
relation of the customer. - We insert two orders related to the customer by calling
.insert()
on theorders
relation.
Retrieving Related Records
Fetching related records is just as straightforward. Let's say you want to find a customer by name and then retrieve their related orders:
const customer = await customerRepo.findFirst({ name: 'Abshire Inc' })
const orders = await customer._.relations.orders.find()
In this code:
- We search for a customer with the specified name using
customerRepo.findFirst()
. - Once we have the customer instance, we access their
orders
relation withcustomer._.relations.orders
. - We use
.find()
to retrieve all related orders associated with the customer.
Using the activeRecord
pattern and direct access to relations simplifies the management of related data, making it more intuitive and efficient.
Many-to-Many
In Remult, you can effectively handle many-to-many relationships between entities by using an intermediate table. This approach is especially useful when you need to associate multiple instances of one entity with multiple instances of another entity. In this section, we'll walk through the process of defining and working with many-to-many relationships using this intermediate table concept.
Entity Definitions:
To illustrate this concept, let's consider two entities: Customer
and Tag
. In this scenario, multiple customers can be associated with multiple tags.
@Entity('customers')
export class Customer {
@Fields.cuid()
id = ''
@Fields.string()
name = ''
@Fields.string()
city = ''
}
@Entity('tags')
export class Tag {
@Fields.cuid()
id = ''
@Fields.string()
name = ''
}
Intermediate Table
To establish this relationship, we'll create an intermediate table called tagsToCustomers
. In this table, both customerId
and tagId
fields are combined as the primary key.
@Entity<TagsToCustomers>('tagsToCustomers', {
id: {
customerId: true,
tagId: true,
},
})
export class TagsToCustomers {
@Fields.string()
customerId = ''
@Fields.string()
tagId = ''
@Relations.toOne<TagsToCustomers, Tag>(() => Tag, 'tagId')
tag?: Tag
}
To uniquely identify associations between customers and tags in a many-to-many relationship, we use the combined
customerId
andtagId
fields as the primary key, specified using the 'id' option in the@Entity
decorator.In this scenario, we've defined a
toOne
relation to theTag
entity within theTagsToCustomers
entity to efficiently retrieve tags associated with a specific customer. This approach simplifies the management of many-to-many relationships while ensuring unique identification of each association.
Now, let's enhance our customer entity with a toMany relationship, enabling us to fetch all of its associated tags effortlessly.
@Entity('customers')
export class Customer {
@Fields.cuid()
id = ''
@Fields.string()
name = ''
@Fields.string()
city = ''
@Relations.toMany(() => TagsToCustomers, 'customerId')
tags?: TagsToCustomers[]
}
Working with Many-to-Many Relationships
Let's explore how to interact with many-to-many relationships using an intermediate table in Remult.
1. Adding Tags to a Customer:
To associate a tag with a customer, consider the follow code:
const tags = await remult
.repo(Tag)
.insert([
{ name: 'vip' },
{ name: 'hot-lead' },
{ name: 'influencer' },
{ name: 'manager' },
]) // Create the tags
const customerRepo = remult.repo(Customer)
const customer = await customerRepo.findFirst({ name: 'Abshire Inc' })
await customerRepo
.relations(customer)
.tags.insert([{ tag: tags[0] }, { tag: tags[2] }])
Here's an explanation of what's happening in this code:
We first insert some tags into the "tags" entity.
We then create a repository instance for the "customer" entity using
remult.repo(Customer)
.We retrieve a specific customer by searching for one with the name "Abshire Inc" using
customerRepo.findFirst({ name: "Abshire Inc" })
. Thecustomer
variable now holds the customer entity.To associate tags with the customer, we use the
relations
method provided by the repository. This method allows us to work with the customer's related entities, in this case, the "tags" relation to the TagsToCustomers entity.Finally, we call the
insert
method on the "tags" relationship and provide an array of tag objects to insert. In this example, we associate the customer with the "vip" tag and the "influencer" tag by specifying the tags' indices in thetags
array.
2. Retrieving Tags for a Customer: To fetch the tags associated with a specific customer:
Certainly, here's a shorter explanation:
const customer = await customerRepo.findFirst(
{ name: 'Abshire Inc' },
{
include: {
tags: {
include: {
tag: true,
},
},
},
},
)
In this code, we're querying the "customer" entity to find a customer named "Abshire Inc." We're also including the related "tags" for that customer, along with the details of each tag. This allows us to fetch both customer and tag data in a single query, making it more efficient when working with related entities.
Resulting Data Structure
Here's an example result of running JSON.stringify
on the customer
object:
{
"id": "fki6t24zkykpljvh4jurzs97",
"name": "Abshire Inc",
"city": "New York",
"tags": [
{
"customerId": "fki6t24zkykpljvh4jurzs97",
"tagId": "aewm0odq9758nopgph3x7brt",
"tag": {
"id": "cf8xv3myluc7pmsgez3p9hn9",
"name": "vip"
}
},
{
"customerId": "fki6t24zkykpljvh4jurzs97",
"tagId": "aewm0odq9758nopgph3x7brt",
"tag": {
"id": "aewm0odq9758nopgph3x7brt",
"name": "influencer"
}
}
]
}
Utilizing an intermediate table for managing many-to-many relationships in Remult allows for a flexible and efficient approach to handle complex data associations. Whether you are connecting customers with tags or other entities, this method provides a powerful way to maintain data integrity and perform queries effectively within your application.
In this guide, we've explored the essential concepts of managing entity relations within the Remult library. From one-to-one to many-to-many relationships, we've covered the declaration, customization, and querying of these relations. By understanding the nuances of entity relations, users can harness the full potential of Remult to build robust TypeScript applications with ease.