Test Category

Test Blog Post

Starter template for writing out a blog post using MDX/JSX and Next.js.

No Name Exists

Abdullah Muhammad

Published on May 17, 20265 min read 1 views

Share:
Article Cover Image

Introduction

In this article, we will explore the Drizzle ORM library which is a very useful library for working with relational databases.

It is similar to the Sequelize ORM library which we explored when we touched on AWS RDS, a cloud relational database service.

The beauty about Drizzle is that the learning curve is low.

If you are familiar with relational databases and concepts related to SQL such as tables, records, primary/foreign keys, database constraints, DML/DDL, and models, you should be well equipped to learn Drizzle.

In this article, we will cover:

  • Drizzle configuration
  • Creating schemas
  • Connecting with Supabase
  • Migrations
  • Advantages over the simple Supabase-js library

Note: The Supabase client library is good for basic queries and type safety is optional. We have not explored Supabase in an article, but we will in a future one.

Drizzle offers you the ability to create complex queries while guaranteeing type safety along the way.

Drizzle enhances security because queries run on the server which ensures that there is safety and privacy of data when queries are being processed.

As an ORM tool, it offers developers the ability to create SQL-like queries that can be programmatically performed on database tables.


SQL and Database Refresher

The documentation concerning Drizzle is thorough and comprehensive so there is no need to go through the basic concepts.

You can read more about them in their official docs.

We will cover database interactions. More importantly, the CRUD operations.


SQL Dialects and Nomenclature

If you have worked with MySQL or any variation of SQL (there are tons of dialects out there), you will have come across a concept of "enforcing schema".

In this article, we will work with Supabase which is a scalable PostgreSQL database which, more or less, is similar to MySQL.

Various objects can exist within a relational database. They could represent different things such as:

  • Indexes
  • Tables
  • Views
  • Stored Procedures

In fact, SQL has a subset language used to create these objects known as Data Definition Language (DDL).

Data is stored in tables and that is where the "enforcing schema" concept comes into play.

When we define table structure using Drizzle, we are essentially setting rules that dictate what data can be inserted into the table.

"What describes how we can interact with these objects?"

Aside from DDL, there is another subset language in SQL that describes how we can interact with database objects and it is known as Data Manipulation Language (DML).

That is where the basic CRUD operations come into view. We write commands against the table to "manipulate" data stored there.

"Great! But so far, you have talked about SQL. How does this relate to Drizzle?"

Drizzle is an ORM which allows one to map table attributes as an object which we can programmatically run queries against.

There is no need to run SQL code as these are considered the equivalent of running DML statements.

Drizzle ORM is very similar to Sequelize so if you have worked with it or any other ORM tool, this should be straight forward.

Drizzle Deep Dive

You can follow along by cloning this repository. The directory we will work with is demos/Demo65_Drizzle_Supabase/next-app.

We will look at all the required Drizzle dependencies needed to kickstart development and proceed looking at configuration, schemas, migrations, and most importantly, queries.


Drizzle ORM Configuration

To get started, we need to install the following packages:

npm install drizzle-orm postgres drizzle-kit @types/pg pg

Once that is out of the way, we need to set it up in a custom drizzle.config.ts file which lies in the root location defined below /src/drizzle.config.ts:

GitHub GistTypeScript
import { defineConfig } from 'drizzle-kit';

// Defining the configuration setup for Drizzle ORM
// Specify the SQL dialect we will work with, since this is Supabase, we work with postgresql
export default defineConfig({
  schema: './db/schema/User.ts',
  out: './drizzle',
  dialect: 'postgresql',
  dbCredentials: {
    url: process.env.DATABASE_URL!
  }
});
drizzle.config.ts file containing all the necessary configurations to be setup correctly

We define a very simple configuration file which details the location of the schemas we are working with as well as the SQL dialect.

Simple and straight forward.


Drizzle Supabase Connection

In order for Drizzle to know which database to perform operations, we must define and establish a database connection.

The following file details that /src/db/database.ts:

GitHub GistTypeScript
import { drizzle } from 'drizzle-orm/postgres-js';
import postgres from 'postgres';
import * as schema from './schema/User';

// Database connection string to establish connection to Supabase
// Database connection using an ORM is slightly different 
// We use the database URL to connect directly to Supabase
const connectionString = process.env.DATABASE_URL!;

// Disable prefetch as it is not supported for "Transaction" pool mode
const client = postgres(connectionString, { prepare: false });

// Export the database instance using drizzle
export const db = drizzle(client, { schema });
Database connection file details setup required for working with Supabase

Now, with every query request, Drizzle will have access to the appropriate database.

Since we are working with Supabase and accessing it via an ORM, the connection setup is slightly different. We only need to specify the DATABASE_URL to connect to it.

When working with the Supabase client library, you will need an ANON_KEY along with the DATABASE_URL to connect to it.


Drizzle Schemas

This is the heart of it all. Inside the db directory, you will find a sub-directory named schemas.

In the schemas directory, you will find a simple file which defines the User model (or, in this case, a table) detailing all the related attributes associated with it.

This defines what records are acceptable in the User table. Insertion of records that do not match this criteria will be rejected.

Remember: Relational databases enforce schemas so data must adhere to it.

The following file details the User schema src/db/schema/User.ts:

GitHub GistTypeScript
import { pgTable, serial, text, timestamp } from "drizzle-orm/pg-core";

// Creating and exporting a User table schema
// Utilizing built-in functions for setting the attribute names, column conditions, constraints
export const Users = pgTable("Users", {
  id: serial("id").primaryKey(),
  name: text("name").notNull(),
  email: text("email").notNull().unique(),
  createdAt: timestamp("created_at").defaultNow()
});
User.ts file detailing the User schema

We use built-in functions such as text, serial, and timestamp to set the different data types.

We can also add constraints to each of the table attributes using functions such as primaryKey, notNull, and unique.


Drizzle Migrations

Supabase needs to understand what tables it is working with. In the past, we would have a separate directory dedicated to making migrations to the database.

Instead, we can simply run a command to migrate local data to the database.

We can define a set of scripts as seen in this package.json file definition:

GitHub GistJSON
{
  "name": "nextapp",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "db:push": "drizzle-kit push",
    "db:studio": "drizzle-kit studio",
    "db:generate": "drizzle-kit generate",
    "db:migrate": "drizzle-kit migrate",
    "dev": "next dev",
    "build": "next build",
    "start": "next start",
    "lint": "next lint"
  },
  "dependencies": {
    "drizzle-kit": "^0.31.4",
    "drizzle-orm": "^0.44.3",
    "next": "15.4.2",
    "pg": "^8.16.3",
    "postgres": "^3.4.7",
    "react": "19.1.0",
    "react-dom": "19.1.0"
  },
  "devDependencies": {
    "@eslint/eslintrc": "^3",
    "@tailwindcss/postcss": "^4",
    "@types/node": "^20",
    "@types/pg": "^8.15.4",
    "@types/react": "^19",
    "@types/react-dom": "^19",
    "eslint": "^9",
    "eslint-config-next": "15.4.2",
    "tailwindcss": "^4",
    "typescript": "^5"
  }
}
package.json file detailing the different Drizzle ORM commands

For simple workflows, you can run npm run db:push and this will allow Drizzle to migrate your local work to the database.

After that, you are good to go.


Drizzle Queries

The back-end makes use of the User schema we defined earlier. We can create a separate file that is dedicated to running the most basic CRUD operations src/db/queries/UserTableQueries.ts:

GitHub GistTypeScript
import { db } from "../database";
import { Users } from "../schema/User";
import { eq } from "drizzle-orm";

// Very simple and easy to understand queries for working with the User table
// Create
export async function createUser(name: string, email: string) {
  await db.insert(Users).values({ name, email });
}

// Read
export async function getUserByEmail(email: string) {
  return await db.select().from(Users).where(eq(Users.email, email));
}

// Update
export async function updateUserName(id: number, newName: string) {
  await db.update(Users).set({ name: newName }).where(eq(Users.id, id));
}

// Delete
export async function deleteUser(id: number) {
  await db.delete(Users).where(eq(Users.id, id));
}
UserTableQueries.ts file detailing the most basic CRUD operations

We can make use of operators provided by the drizzle-orm library such as eq to help structure database queries.

There are loads of operators you get with the drizzle-orm library which can help you create complex database queries.

Conclusion

We explored the Drizzle ORM library in detail and worked with a Supabase database to establish a database connection, create schemas, perform migrations, and run queries.

A section of this article was devoted to refreshing memory on SQL and SQL nomenclature.

Drizzle is a safe, server-side tool that every Next.js developer should be familiar with.

You get the added benefit of type-safety by having the ability to write queries in pure TypeScript. No need to ever write SQL code.

In the list below, you will find links to the GitHub repository, Drizzle ORM docs, and the official Supabase docs:

I hope you found this article helpful and look forward to more in the future.

Thank you!

No Name

Abdullah Muhammad

Blogger. Software Engineer. Designer.

Subscribe to the newsletter

Get new articles, code samples, and project updates delivered straight to your inbox.