API with NestJS #170. Polymorphic associations with PostgreSQL and Drizzle ORM

NestJS SQL

This entry is part 170 of 174 in the API with NestJS

It’s not uncommon for a single table to be related to multiple similar tables. A good example is a comment the user can write under a photo, an article, or an audio file.

A possible approach would be to create a separate table for each type of comment, such as or . Unfortunately, this would duplicate much of our code since a comment under a photo would work the same as a comment under an article.

Polymorphic associations

Alternatively, we can implement a polymorphic association. It’s a design pattern that allows a single table to be associated with one of various different tables. This way, we create only a single table. The crucial thing is that a single comment can be related to either a photo or an article, but not both.

An incorrect way to design a polymorphic association

Let’s say that we have a database with articles and photos.

database-schema.ts

The most straightforward way to implement a polymorphic association is through a single column, such as , pointing to either a photo or an article.

database-schema.ts

While this could work, it has multiple downsides. PostgreSQL treats the targetId as a regular number and can’t guarantee that it points to a valid photo or article. Because of that, we would have to manually ensure the database’s integrity. For example, when we delete a photo, we must remember to delete all related comments.

A better way solution

Let’s create a schema with separate and columns.

database-schema.ts

If you want to know more about designing relationships with the Drizzle ORM, check out API with NestJS #154. Many-to-many relationships with Drizzle ORM and PostgreSQL

Now, Drizzle ORM will create two separate foreign key constraints, each based on a separate column. Let’s examine the SQL migration it generates.

0001_create-comments-table.sql

It’s crucial to notice that both the and columns are nullable. Because of that, we could have a comment that is not related to either an article or a photo.

Adding the check constraint

We can fix the above issue with a check constraint.

If you want to know more about constraints with the Drizzle ORM, check out API with NestJS #152. SQL constraints with the Drizzle ORM

Since the Drizzle ORM does not support check constraints out of the box, we must adjust the default migration generated by the Drizzle Kit.

0001_create-comments-table.sql

Thanks to adding the constraint at the bottom of our migration, PostgreSQL now ensures that precisely one of the and columns does not contain a null value. To achieve that, we use the function built into PostgreSQL.

Validation

We must ensure the user provides exactly one of the and properties. To do that, we can create a custom decorator for the library.

create-comment.dto.ts

Our decorator checks if the value is an integer and ensures that the user provides precisely one of the and properties.

Alternatively, when the user creates the comment, we can detect if the constraint is violated.

comments.service.ts

Thanks to the above solution, we can create comments associated with photos or articles but not both. Also, if the user tries to make a comment that is not related to either articles or photos, they will see an appropriate error message as well.

Summary

In this article, we implemented a polymorphic association using the Drizzle ORM and PostgreSQL while maintaining the integrity of our database.

With polymorphic associations, we have a lot of flexibility, which allows us to simplify the schema of our database by reducing the number of SQL tables we need. However, they aren’t perfect for every situation. Some might say that having a single table that handles multiple different relationships makes the structure less clear, making it tougher to debug and maintain – especially for developers unfamiliar with this pattern. Though polymorphic associations can make our database schema more flexible, we must use them carefully since implementing them requires a thorough understanding of this pattern.

Series Navigation<< API with NestJS #169. Unique IDs with UUIDs using Drizzle ORM and PostgreSQLAPI with NestJS #171. Recursive relationships with Drizzle ORM and PostgreSQL >>
Subscribe
Notify of
guest
0 Comments
Inline Feedbacks
View all comments