Adopting Drizzle ORM as my go-to ORM for TypeScript

Tags:
  • TypeScript
  • Node.js
  • Drizzle ORM
  • Prisma
  • ORMs

Introduction

ORM stands for Object-Relational Mapping. It's a programming technique that allows you to query and manipulate data from a database (SQL) using an object-oriented paradigm. In other words, it allows you to interact with a database using objects instead of raw SQL queries. This obviously has its pros and cons, but it's a very popular technique in modern web development.

One of the main benefits of using an ORM is the speed of development, ease of use, and the ability to work with objects directly instead of raw SQL queries. On the contrary, one of the main drawbacks in my opinion is the lack of understanding of how the database works under the hood and how to write SQL queries. Writting SQL queries, forces you to think about how the data is structured and helps you understand the database schema better.


Key takeaways (TL;DR)

  • Drizzle ORM is a new ORM for TypeScript that I've been using lately and I'm really enjoying it.
  • There is no one-size-fits-all solution when it comes to ORMs, but Drizzle ORM is a great choice for TypeScript projects that are crazy about type safety.
  • Has top-notch performance.
  • Has feature rich SQL dialects.
  • Has zero dependencies.
  • Does not involve code generation.
  • Is lightweight & edge ready.

There are many ORMs out there for TypeScript, so it's true that there is something for everyone. Picking the right one for your project can be a bit overwhelming, but it's important to choose the one that best fits your needs. Most of the times, you just pick the one you are most comfortable with or the one you have used in the past.

Some of the most popular ones are:

Just a disclaimer, I have not used all of them, so I can't provide a detailed comparison. I have used Prisma, TypeORM, and Drizzle ORM. In this article, we're going to discuss and compare mostly Drizzle ORM and Prisma simply because I have more experience with them.

Why Drizzle ORM over something else?

Personally, I've been using Prisma for the past year and I've been very happy with it. It's a great ORM that allows you to define models and relationships using a Prisma language which is actually very nice.

I made the switch because I heared really smart people talking about Drizzle ORM and I wanted to give it a try. Initially I used it for small projects, but I was so impressed with the performance and the type safety that I decided to use it for all my TypeScript projects. The things I absolutely love about Drizzle ORM are:

  • Type safety: Drizzle ORM is built with TypeScript in mind. It has a very strong type system that allows you to write type-safe code.
  • Performance: Drizzle ORM is very fast. It's built with performance in mind and it's optimized for speed.
  • SQL dialects: Drizzle ORM has a rich set of SQL dialects that allow you to write complex queries with ease.
  • Zero dependencies: Drizzle ORM has zero dependencies. It's a standalone library that you can use in your projects without worrying about dependencies.
  • Lightweight & edge ready: Drizzle ORM is lightweight and it's ready for the edge. It's built with modern web technologies in mind and it's optimized for performance.

I would say that I started with those five reasons, but honestly I stayed because I was an SQL noob and I thought it's a great excuse to learn SQL. By learning SQL, I was able to write more complex queries and understand how the database works under the hood. The goal was never to become a database genius, but to improve my skillset and become a better developer.

I have a confession to make...

Before getting into Drizzle ORM, I was a big fan of Prisma. I still am, but I've been living in a utopia where I was using Prisma language to define models and relationships. I was shipping code faster than ever, but I was missing something. Honestly I did not know what Prisma does under the hood and what an SQL join is. I was just using the Prisma client to query the database and that was it. I was working far away from the database and I was not thinking about how the data is structured and how to write SQL queries.

Obviously this is not a bad thing (depending on who you are), but if you have this curiosity to learn how the database works under the hood, then you should consider using an ORM that allows you come closer to SQL queries. The end goal is not to replace ORM with raw SQL queries, but to have a better understanding of how the database works and how to write queries that are somewhere in the middle of ORM and raw SQL queries.

My Favorite Features of Drizzle ORM

Let's see some of the features of Drizzle ORM that make it a great choice for TypeScript projects. Obviously, there are many other features that I'm not going to cover here, but I'm going to focus on the ones that I think are the most important.

1. SQL dialects

Assuming you want to learn SQL, you can use Drizzle ORM which in my opinion is the sweet middle ground between an ORM expeience and raw SQL queries.

Let's see an example of querying the database using Drizzle ORM:

const [user] = await db
  .select()
  .from(users)
  .where(eq(users.email, session.user.email!))
  .limit(1);

As we can see, there is not an actual SQL query here, but we are using a SQL-like syntax to query the database. Since SQL returns rows, we are destructuring the result and getting the first row.

Basically we're doing something like this:

SELECT * FROM users WHERE email = '...' LIMIT 1;

This is a very simple example, but you got the idea. You can write more complex queries using Drizzle ORM and you can use the SQL dialects to write queries that are closer to raw SQL queries.

2. The magic SQL operator

People often look for ways to write raw SQL queries for numerous reasons like performance, flexibility, and more. While Prisma supports raw SQL queries, it's not as straightforward as Drizzle ORM simply because Drizzle ORM has JavaScript objects within the template literal string that allow you to write raw SQL queries in a more type-safe way.

Let's see an example of a raw SQL query using Drizzle ORM:

import { sql } from 'drizzle-orm' 

const id = 69;
await db.execute(sql`select * from ${usersTable} where ${usersTable.id} = ${id}`)

In the above example you can see that we don't implicitly pass the table name and the column name as strings, but we use the usersTable object that we have defined earlier. Let's see an example of how usersTable is defined:

export const usersTable = pgTable(
  'users',
  {
    id: serial('id').primaryKey(),
    name: text('name').notNull(),
    email: text('email').notNull(),
    emailVerified: timestamp('emailVerified', { mode: 'date' }),
    stripeCustomerId: text('stripeCustomerId').notNull(),
    passwordResetToken: text('passwordResetToken'),
    passwordResetTokenExpires: timestamp('passwordResetTokenExpires', { mode: 'date' }),
    password: text('password').notNull(),
    createdAt: timestamp('createdAt').defaultNow().notNull(),
    updatedAt: timestamp('updatedAt').defaultNow().notNull(),
  },
  (users) => {
    return {
      uniqueIdx: uniqueIndex('unique_idx').on(users.email),
    };
  },
);

3. Type safety

Drizzle ORM is built with TypeScript in mind. It has a very strong type system that allows you to write type-safe code. This is very important because it helps you catch errors at compile time instead of runtime. I usually combine it with Zod, a TypeScript-first schema declaration and validation library, to validate the data before inserting it into the database. Let's be real, not all compile time errors can be caught, so it's always good to have some runtime checks as well.

Let's see an example of how awesome type safety is in Drizzle ORM. In this example we're looking into a registration form and how we can validate the data before inserting it into the database.

// Server
import { z } from 'zod';

const RegisterSchema = z
  .object({
    email: z.string().email(),
    password: z.string().min(6).max(100),
    passwordRetype: z.string().min(6).max(100),
  })
  .refine((data) => data.password === data.passwordRetype, {
    message: 'Passwords do not match.',
    path: ['passwordRetype'],
  });

/**
 * We take in the fields from the client (in this case via a Next.js server action) and we get intellisense in them.
 * We have to validate the fields on runtime, but we get compile time type safety.
 * Then we return a custom type of the user that we want to insert into the database.
 * 
 * We use a `Partial` type because we don't want to return the password in the response (for security reasons).
*/
export async function registerUser(
  fields: z.infer<RegisterSchema>
): Promise<ServerActionResponse<Partial<typeof users.$inferInsert>>>;

4. Drizzle Kit

This is a CLI tool that helps you run migrations easily. It's very simple, but it gets the job done. You can run migrations, create new migrations, and rollback migrations with a single command. You can either use as a CLI or run it programmatically in your code.

I greatly appreciate having .sql files for migrations, I find it easier to read and understand what's going on as time goes by. Don't hesitate to read the docs, they are very well written and you can find everything you need there.


Conclusion

It took me a while to understand the hype around Drizzle ORM, but now I can say that I'm a big fan of it. Sometimes even if you are happy with something, it's good to try new things and see if there is something better out there. In the end of the day, it's all about personal preference and what works best for you and your team.

I did not mention anything about running Drizzle ORM on the edge, but I can tell you that it's very easy to do so. I just have not found myself in a situation where I had to run it on the edge, but I'm sure it's going to be a breeze. In the future, I'm planning to integrate it with Cloudflare Workers that run queries against D1 databases.

Thank you for reading this article. I hope you found it useful and that you learned something new about Drizzle ORM. Don't hesitate to reach out to me if you have any questions or if you want to share your experience with Drizzle ORM.

My email is [email protected].

Further reading