Drizzle: A Lightweight, Type-Safe ORM That Loves SQL
Most real-world applications rely on relational databases like MySQL, PostgreSQL, and SQLite to store critical user data. But working with raw SQL can be painful—even life-threatening when you screw up. Over the decades, hundreds of libraries have been built to abstract away SQL code, hiding its complexity inside object-oriented programming languages. This technique is called Object Relational Mapping (ORM). While ORMs are great, they often bring unnecessary performance overhead and leaky abstractions because developers lose touch with the underlying SQL. Enter Drizzle—a modern ORM that takes a different approach.
What Is Drizzle?
Drizzle is a lightweight set of tools that makes type-safe object-relational mapping fun again. Instead of hiding SQL behind complex abstractions, Drizzle provides a TypeScript API that closely matches native underlying SQL code. This gives you the benefits of type safety and IntelliSense without sacrificing control or performance.
How Drizzle Works
When every database is different, how does Drizzle maintain such a clean and accurate API? Instead of jamming everything into a single interface, Drizzle provides dedicated adapters for each database (PostgreSQL, MySQL, SQLite, etc.), along with tools for popular cloud hosts like RDS and Neon.
If writing raw SQL-like queries feels too low-level for you, Drizzle also offers an optional Prisma-like query API that keeps data fetching and join code concise and readable.
Drizzle Toolkit
- Drizzle Kit: A CLI for handling database migrations.
- Drizzle Studio: A GUI you can run locally to manage your data.
Getting Started with Drizzle and Neon
To get started, you’ll need a relational database. Neon—a serverless Postgres platform with a generous free tier—is an excellent choice. It’s easy to use and even has a fast serverless driver with a Drizzle adapter.
Both Drizzle and Neon are designed to run on any JavaScript runtime (Node.js, Bun, Deno) as well as edge runtimes like Cloudflare Workers.
After installing Drizzle and your database driver, connect to the database in your TypeScript code. Then, define your schema using the pgTable function. Each table has a name and columns defined with constraints using JavaScript functions that match their SQL equivalents.
You can reference tables in other tables to create relationships with foreign keys. Optionally, use the relations function to simplify relational queries and joins.
Convert your TypeScript schema into PostgreSQL by running drizzle-kit generate. This creates migration files that you can apply to Neon. Now you’re ready to use Drizzle to insert and query data—with full type safety and intuitive relational support.
Conclusion
Drizzle offers a refreshing take on ORMs—embracing SQL instead of abstracting it away. With strong typing, dedicated adapters, and a powerful yet simple API, it brings joy back to database management.
Shout out to Neon for making this tutorial possible. Be sure to check out their Drizzle tutorial to learn more!
Getting Started with Drizzle ORM
If you're considering Drizzle ORM for your next project, this guide will give you the foundation you need. We’ll cover everything from defining schemas and generating migrations, to working locally, integrating with CI/CD pipelines, and deploying to production. Plus, we’ll explore how Neon fits into the workflow with features like database branching. Let’s dive in.
What is Drizzle ORM?
Drizzle ORM is a TypeScript-first ORM with three main components:
- The ORM – offers both relational and SQL-like query APIs.
- Drizzle Kit – the migration tool for generating and managing database migrations.
- Drizzle Studio – a GUI for exploring your data.
Defining Schemas
In Drizzle, you define your database schema in TypeScript. For example, a users table
can be created with columns like id, name, and
email. You can also define relations such as posts belonging to users
and comments belonging to both posts and users. While these relations are defined at the application level, any database-level
constraints must be explicitly set in the schema itself.
Migrations with Drizzle Kit
Once your schema is defined, you use Drizzle Kit to generate migrations. These migrations translate
TypeScript schema changes into SQL files. For example, adding createdAt and
updatedAt fields to a table generates a new migration automatically.
Migrations can then be applied locally, in staging environments, or in production.
Local Development Workflow
During development, you define schemas, generate migrations, and apply them to a local Postgres instance (or a Neon branch). When you’re ready, you push your code and migrations to version control, where they run in your CI/CD pipeline against a preview or staging database. Once merged, migrations are applied to your production database.
Querying Data
Drizzle offers two query styles:
- SQL-like API – write familiar SQL-style queries with full TypeScript type safety.
- Relational API – query across multiple related tables easily. For example, fetching posts with their authors and comments becomes a single, type-safe query.
Since queries are fully type-safe, any mismatch between schema and query parameters results in clear compile-time errors. This makes development faster and safer, without relying on code generation.
Why Use Drizzle with Neon?
Neon provides serverless Postgres with database branching, making it a natural fit with Drizzle. You can spin up branches for local development, preview deployments, and staging environments—all integrated into your workflow. Drizzle’s ability to plug into Neon’s serverless driver makes querying fast and seamless in serverless and edge environments.
Conclusion
Drizzle ORM gives you a modern, type-safe, and flexible way to work with relational databases in TypeScript. Combined with Neon, it supports workflows that scale from local development to production. Whether you prefer SQL-like syntax or a relational API, Drizzle keeps your queries safe, predictable, and efficient.
Have questions? Reach out in the Neon Discord community—we’d love to hear from you.
Considering Drizzle ORM for your next project? This guide walks you through the core concepts of defining your schema, generating migrations, writing type-safe queries, and integrating it all into a seamless local and CI workflow, with a special focus on using it with Postgres via Neon's serverless driver and branching capabilities.
The Three Pillars of Drizzle
Drizzle consists of three main components:
- • Drizzle ORM: The core library offering both a relational and a SQL-like query API.
- • Drizzle Kit: A companion CLI tool used to generate database migrations.
- • Drizzle Studio: A GUI for exploring and managing the data inside your database.
The Development Workflow
The typical workflow when using Drizzle follows these steps:
- 1. Define Schema: You first define your database schema using TypeScript.
- 2. Generate Migration: From the TypeScript definition, Drizzle Kit generates the necessary SQL migration files.
- 3. Apply Locally: Apply the migration to your local Postgres instance or a Neon branch.
- 4. Preview & Deploy: In your CI/CD pipeline, apply the migration to a preview/staging database (e.g., a Neon branch) and finally to your production database upon merge.
Defining Your Schema
Schemas are defined in TypeScript using Drizzle's `pgTable` function. This provides full type safety from the very beginning without any code generation.
// Example schema definition for a 'users' table
export const users = pgTable('users', {
id: serial('id').primaryKey(),
name: text('name').notNull(),
email: text('email').notNull(),
createdAt: timestamp('created_at').defaultNow(),
updatedAt: timestamp('updated_at').defaultNow(),
});
You can define foreign keys and relations directly in the schema, enabling powerful query capabilities later.
Generating & Running Migrations
After defining your schema, use Drizzle Kit to generate migrations. A script like `bun run db:generate` can execute the command:
drizzle-kit generate --schema=src/db/schema.ts
To apply the generated migrations, you use Drizzle's `migrate` function. A key strength of Drizzle is that it doesn't ship with its own database client; you bring your own. For Neon, you use the serverless driver.
import { drizzle } from 'drizzle-orm/neon-serverless';
import { migrate } from 'drizzle-orm/neon-serverless/migrator';
// Establish connection and run migrations
const db = drizzle(connection);
await migrate(db, { migrationsFolder: './src/db/migrations' });
Writing Type-Safe Queries
Drizzle offers two primary APIs for querying your database:
1. SQL-like API
This API provides a familiar, expressive way to write queries with full type safety and autocompletion.
// Inserting data is fully type-checked
await db.insert(users).values([
{ name: 'Alice', email: 'alice@example.com' },
{ name: 'Bob', email: 'bob@example.com' },
]);
// Selecting data
const allUsers = await db.select().from(users);
2. Relational API
For more complex queries involving multiple tables, the relational API simplifies fetching nested data. This requires defining relations in your schema.
// Fetch all posts with their author and comments
const postsWithRelations = await db.query.posts.findMany({
with: {
author: true,
comments: true,
}
});
Conclusion
Drizzle ORM provides a robust, type-safe foundation for working with databases in TypeScript. Its intuitive schema definition, powerful migration tools, and flexible query APIs make it an excellent choice for modern application development. Combined with Neon's serverless Postgres and branching features, you can create a highly efficient development and deployment workflow for both preview environments and production.
Comments
Post a Comment