API with NestJS #7. Creating relationships with Postgres and TypeORM

JavaScript NestJS TypeScript

This entry is part 7 of 121 in the API with NestJS

When we build an application, we create many entities. They often somehow relate to each other, and defining such relationships is an essential part of designing a database. In this article, we go through what is a relationship in the context of a Postgres database and how do we work with them using TypeORM and NestJS.

The relational databases have been around for quite some time and work great with structured data. They do so by organizing the data into tables and linking them to each other. When running various SQL queries, we can join the tables and extract meaningful information. There are a few different types of relationships, and today we go through them with the use of examples.

We’ve also gone through it in the TypeScript Express series. The below article acts as a recap of what we can get from there. This time we also look more into the SQL queries that TypeORM generates

You can find all of the code from this series in this repository.

One-to-one

With the one-to-one relationship, the first table has just one matching row in the second table, and vice versa.

The most straightforward example would be adding an address entity.

users/address.entity.ts

Let’s assume that one address can be linked to just one user. Also, a user can’t have more than one address.

To implement the above, we need a one-to-one relationship. When using TypeORM, we can create it effortlessly with the use of decorators.

users/user.entity.ts

Above, we use the   decorator. Its argument is a function that returns the class of the entity that we want to make a relationship with.

The second decorator, the  , indicates that the   entity owns the relationship. It means that the rows of the  table contain the  column that can keep the id of an address. We use it only on one side of the relationship.

We can look into pgAdmin to inspect what TypeORM does to create the desired relationship.

Above, we can see that the   is a regular integer column. It has a constraint put onto it that indicates that any value we place into the  column needs to match some id in the  table.

The above can be simplified without the   keyword.

Both   and   are a default behavior. They indicate that Postgres will raise an error if we attempt to delete or change the id of an address that is currently in use.

The   refers to a situation when we use more than one column as the foreign key. It means that we allow some of them to be null.

Inverse relationship

Currently, our relationship is unidirectional. It means that only one side of the relationship has information about the other side. We could change that by creating an inverse relationship. By doing so, we make the relationship between the User and the Address bidirectional.

To create the inverse relationship, we need to use the   and provide a property that holds the other side of the relationship.

users/address.entity.ts

The crucial thing is that the inverse relationship is a bit of an abstract concept, and it does not create any additional columns in the database.

 

Storing the information about both sides of the relationship can come in handy. We can easily relate to both sides, for example, to fetch the addresses with users.

If we want our related entities always to be included, we can make our relationship eager.

Now, every time we fetch users, we also get their addresses. Only one side of the relationship can be eager.

Saving the related entities

Right now, we need to save users and addresses separately and this might not be the most convenient way. Instead, we can turn on the cascade option. Thanks to that, we can save an address while saving a user.

One-to-many and many-to-one

The one-to-many and many-to-one is a relationship where a row from the first table can be linked to multiple rows of the second table. Rows from the second table can be linked to just one row of the first table.

The above is a very fitting relationship to implement to posts and users that we’ve defined in the previous parts of this series. Let’s assume that a user can create multiple posts, but a post has just one author.

users/user.entity.ts

Thanks to using the   decorator, one user can be linked to many posts. We also need to define the other side of this relationship.

Thanks to the   decorator, many posts can be related to one user.

We implemented the authentication in the third part of this series. When a post is created in our API, we have access to the data about the authenticated user. We need to use it to determine the author of the post.

If we want to return a list of the posts with the authors, we can now easily do so.

If we look into the database, we can see that the side of the relationship that uses   decorator stores the foreign key.

This means that the post stores the id of the author and not the other way around.

Many-to-many

Previously, we added a property called category to our posts. Let’s elaborate on that more.

We would like to be able to define categories reusable across posts. We also want a single post to be able to belong to multiple categories.

The above is a many-to-many relationship. It happens when a row from the first table can link to multiple rows from the second table and the other way around.

categories/category.entity.ts

posts/post.entity.ts

When we use the   and   decorators, TypeORM set ups an additional table. This way, neither the Post nor Category table stores the data about the relationship.

Above, we can see that our new   table uses a primary key that consists of the   and   combined.

We can also make the many-to-many relationship bidirectional. Remember to use the JoinTable decorator only on one side of the relationship, though.

Thanks to doing the above, we can now easily fetch categories along with their posts.

Summary

This time we’ve covered creating relationships while using NestJS with Postgres and TypeORM. It included one-to-one, one-to-many, and many-to-many. We supplied them with various options, such as   and  . We’ve also looked into SQL queries that TypeORM creates, to understand better how it works.

Series Navigation<< API with NestJS #6. Looking into dependency injection and modulesAPI with NestJS #8. Writing unit tests >>
Subscribe
Notify of
guest
12 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Duc Lux
3 years ago

TYSM for this helpfull, Can you write article about Grapql, Typeorm, nestjs

babou
babou
3 years ago

When I create a user with address in the body, it works. (I enabled the cascade parameter)
But when trying to update a user (which has not address yet) (PUT) with address in the body, it didn’t work.

I can’t figure out why.

Ignacio Ruiz
Ignacio Ruiz
3 years ago

can i post an address with a user inside? like 
{
  “street”: “test”,
“user”: {
“name”: “John”
}
}

Goutham
Goutham
3 years ago

How to save many-to-many relationships

Tamerlan
Tamerlan
3 years ago
Reply to  Goutham

Here’s the way I did it:
Get a list of category ids from user. (from request body, or createPostDto)
Once you have that then, you need to find all categories with the list of ids

Then you link the post with categories

Stephanie
Stephanie
2 years ago
Reply to  Tamerlan

How does your getCategoriesByIds look ?

Solomon
Solomon
2 years ago
Reply to  Stephanie

Mine looks like this

Last edited 2 years ago by Solomon
Mike
Mike
3 years ago

What if I want not to update the category, but add another one? Which method should I use?

Constantin
Constantin
3 years ago

Hi
Can you help me please?
I hav an entity:

Also a DTO:

And a service when i’m try to update the record if it exists or create if not:

It doesn’t work because of error:

I really don’t understand this error…

Abdullah Alsalem
3 years ago

how to add column “created_by” in entity? please notice it is refer to “user_id” in the same entity?

Rado
Rado
2 years ago

I have a problem with updating Post error message below

Cannot query across many-to-many for property categories

huy
huy
2 years ago

How to delete categories record with many to many bidirectional?