API with NestJS #169. Unique IDs with UUIDs using Drizzle ORM and PostgreSQL

NestJS

This entry is part 169 of 173 in the API with NestJS

All rows in our database need unique identifiers, typically a sequence of numbers. Alternatively, we can use Universally Unique Identifiers (UUID). In this article, we explore the pros and cons of UUIDs and show how to implement them in a project that uses NestJS, PostgreSQL, and the Drizzle ORM.

Introducing UUIDs

At this point, we’re used to the decimal numeral system, which uses ten symbols (0-9) for values. However, there are alternatives, such as the binary system, which uses just two symbols (0 and 1).

Hexadecimal system

The hexadecimal system uses sixteen symbols ranging from 0 to 9 and from A to F, which is good for representing big numbers. Because of that, UUIDs are hexadecimal numbers that can contain a value that’s over 340 undecillion. One undecillion is a number equal to 1, followed by 36 zeroes.

Thanks to the hexadecimal system, we can shorten the way we write 340 undecillion from 340,000,000,000,000,000,000,000,000,000,000 to .

We often prefix the hexadecimal numbers with to indicate they follow the hexadecimal system.

To make UUIDs easier to use, we store them with dashes that divide the UUID into five groups, such as .

In the hexidecimal system, both uppercase and lowercase characters are valid. They represent the same values, but UUIDs usually use lowercase.

UUIDs can be considered globally unique

There is more than one way to generate a UUID. However, the most common specification is version 4, which generates IDs with pseudo-random numbers.

Most systems generate pseudo-random numbers rather than truly random ones. Computers are deterministic, meaning their operations foillow specific, predictable algorithms.

Generating the same UUID more than once is possible, but the chances are very low. If we generate 103 trillion v4 UUIDs, the chance we create a duplicate is around one in a billion, thanks to a massive number of possible values. This makes it good enough to be considered unique by most applications.

Pros of UUIDs

We probably won’t find a duplicate UUID across multiple tables, databases, or systems. This allows us to merge the data from multiple sources without worrying about colliding IDs. Also, various distributed systems can generate UUIDs independently without running into duplicates.

Debugging and tracing can be more straightforward since UUIDs are unique across all systems. When we see a certain UUID in our logs, we can find the related database row even if we don’t know which table it came from.

When we create a regular sequential ID, we reveal the current number of records. Since UUIDs are random, they don’t have this flaw. This makes it virtually impossible for the attacker to guess the ID of a particular record. Relying solely on security by obscurity is not a good idea, but we could consider it a bonus.

Cons of UUIDs

UUIDs can be a bit harder to read because they are longer and random, as opposed to IDs generated sequentially.

Also, a single UUID takes 16 bytes and is larger than a traditional ID, which typically takes 4 or 8 bytes and can lead to more storage use. Additionally, creating UUIDs requires more computational resources than generating regular, sequential IDs.

UUIDs with the Drizzle ORM

One way of implementing UUIDs with the Drizzle ORM is to use the uuid library. Thanks to the

database.schema.ts

Thanks to the function, we can set up a dynamic default value for a particular column. By pairing it with the uuid library, we get a new, random UUID every time we insert a row into the database using the Drizzle ORM.

Let’s generate a migration that creates our table.

If you want to know more about migrations with the Drizzle ORM, check out API with NestJS #149. Introduction to the Drizzle ORM with PostgreSQL

0000_add-articles-table.sql

The crucial thing to notice is that the database does not generate the UUID values since they are not mentioned in the migration. Instead, we create them using the uuid library.

Generating UUIDs with PostgreSQL

The above approach works fine but has some disadvantages. We might want to create rows outside the Drizzle ORM, for example, using the pgAdmin interface. When this happens, we need to generate the UUID manually.

Alternatively, we can set up PostgreSQL to generate the UUID for us with the function built into PostgreSQL.

database.schema.ts

Now, when we run the migration, we can see that PostgreSQL will provide the default value every time we add a row to our table.

0000_add-articles-table.sql

To use the function in our database, we need the extension. To install it, we need to run a simple SQL query.

Thanks to handling the UUIDs at the database level, we ensure consistency even if various applications interact with our database. We also don’t have to provide the ID manually when running raw SQL queries.

Adjusting our NestJS application to use strings

The UUIDs are represented as strings, and we need to take this into account when implementing our controller. Instead of sending numerical values with query params, our users will send IDs as strings.

articles.controller.ts

Summary

In this article, we learned how Universally Unique Identifiers (UUIDs) work and how they can be an alternative to typical numerical sequences. We explored various ways to generate them through examples in a NestJS application that uses the Drizzle ORM and PostgreSQL. Thanks to learning both the advantages and disadvantages of UUIDs, we now know when it makes sense to implement them in our project.

Series Navigation<< API with NestJS #168. Integration tests with the Drizzle ORMAPI with NestJS #170. Polymorphic associations with PostgreSQL and Drizzle ORM >>
Subscribe
Notify of
guest
0 Comments
Inline Feedbacks
View all comments