Drizzle ORM: En komplett guide til typesikker PostgreSQL
Lær hvordan du mestrer Drizzle ORM med PostgreSQL. Fra skjemadefinisjoner til avanserte queries - alt du trenger for moderne database-utvikling.
Drizzle ORM: En komplett guide til typesikker PostgreSQL
Databasen er hjertet i enhver applikasjon. Men for mange utviklere er database-queries en kilde til frustrasjon: manglende type-sikkerhet, vanskelig debugging, og komplekse ORM-abstraksjoner som skjuler hva som faktisk skjer.
Drizzle ORM endrer dette. Det er en moderne, lettvektig ORM som gir deg full type-sikkerhet uten å ofre ytelse eller kontroll.
I denne guiden lærer du hvordan du mestrer Drizzle ORM med PostgreSQL — fra grunnleggende oppsett til avanserte mønstre.
Hvorfor Drizzle ORM?
Før vi dykker inn i koden, la oss forstå hvorfor Drizzle skiller seg ut:
Lettvektig og rask
Drizzle er kun ~7.4kb minified+gzipped. Det er tree-shakeable med null avhengigheter. Sammenlign dette med Prisma som krever en egen binær og generer megabytes med kode.
SQL som førsteklasses borger
I motsetning til ORMs som prøver å skjule SQL, omfavner Drizzle det:
// Drizzle - du ser nøyaktig hva som skjer
const users = await db
.select()
.from(usersTable)
.where(eq(usersTable.email, email));
// Generert SQL: SELECT * FROM users WHERE email = $1
Full type-sikkerhet
Skjemaet ditt er kilden til sannhet. TypeScript vet nøyaktig hva hver tabell og kolonne inneholder:
// TypeScript fanger opp feil ved kompilering
const user = await db.query.users.findFirst({
where: eq(users.emal, "test@test.com"), // ❌ Error: 'emal' does not exist
});
LLM-First Boilerplate kommer med Drizzle ORM ferdig konfigurert. Sjekk ut kom i gang-guiden for oppsett.
Grunnleggende oppsett
Installer avhengigheter
npm install drizzle-orm postgres
npm install -D drizzle-kit
Konfigurer Drizzle
Opprett drizzle.config.ts i roten av prosjektet:
import type { Config } from "drizzle-kit";
export default {
schema: "./lib/db/schema.ts",
out: "./drizzle",
dialect: "postgresql",
dbCredentials: {
url: process.env.DATABASE_URL!,
},
} satisfies Config;
Definer skjemaet
Her er det Drizzle virkelig skinner. Skjemaet er ren TypeScript:
// lib/db/schema.ts
import { pgTable, text, timestamp, boolean, integer } from "drizzle-orm/pg-core";
export const users = pgTable("users", {
id: text("id").primaryKey(),
email: text("email").notNull().unique(),
name: text("name"),
createdAt: timestamp("created_at").defaultNow().notNull(),
updatedAt: timestamp("updated_at").defaultNow().notNull(),
});
export const posts = pgTable("posts", {
id: text("id").primaryKey(),
title: text("title").notNull(),
content: text("content"),
published: boolean("published").default(false).notNull(),
authorId: text("author_id").references(() => users.id).notNull(),
createdAt: timestamp("created_at").defaultNow().notNull(),
});
2025 Best Practice: Identity columns
PostgreSQL anbefaler nå identity columns over serial types. Drizzle støtter dette fullt ut:
import { pgTable, text, integer } from "drizzle-orm/pg-core";
export const products = pgTable("products", {
id: integer("id").primaryKey().generatedAlwaysAsIdentity({
startWith: 1,
increment: 1,
}),
name: text("name").notNull(),
price: integer("price").notNull(),
});
Bruk generatedAlwaysAsIdentity() for nye prosjekter i stedet for serial().
Dette er den moderne PostgreSQL-standarden.
Database-tilkobling
Opprett tilkoblingen til Neon PostgreSQL eller din foretrukne PostgreSQL-host:
// lib/db/index.ts
import { drizzle } from "drizzle-orm/postgres-js";
import postgres from "postgres";
import * as schema from "./schema";
if (!process.env.DATABASE_URL) {
throw new Error("DATABASE_URL environment variable is required");
}
const client = postgres(process.env.DATABASE_URL);
export const db = drizzle(client, { schema });
CRUD-operasjoner
Create
import { db } from "@/lib/db";
import { users } from "@/lib/db/schema";
const newUser = await db
.insert(users)
.values({
id: crypto.randomUUID(),
email: "bruker@example.com",
name: "Ola Nordmann",
})
.returning();
Read
Drizzle tilbyr to API-er for lesing:
// Query API (anbefalt for de fleste tilfeller)
const user = await db.query.users.findFirst({
where: eq(users.email, "bruker@example.com"),
});
// Select API (for komplekse queries)
const usersWithPosts = await db
.select()
.from(users)
.leftJoin(posts, eq(users.id, posts.authorId))
.where(eq(users.email, "bruker@example.com"));
Update
await db
.update(users)
.set({ name: "Kari Nordmann" })
.where(eq(users.id, userId));
Delete
await db.delete(users).where(eq(users.id, userId));
Avanserte mønstre
Relasjoner
Definer relasjoner for enkel query av relatert data:
// lib/db/schema.ts
import { relations } from "drizzle-orm";
export const usersRelations = relations(users, ({ many }) => ({
posts: many(posts),
}));
export const postsRelations = relations(posts, ({ one }) => ({
author: one(users, {
fields: [posts.authorId],
references: [users.id],
}),
}));
Nå kan du enkelt hente brukere med deres posts:
const userWithPosts = await db.query.users.findFirst({
where: eq(users.id, userId),
with: {
posts: true,
},
});
Transactions
For operasjoner som må være atomiske:
await db.transaction(async (tx) => {
const [user] = await tx
.insert(users)
.values({ id: crypto.randomUUID(), email: "ny@bruker.no" })
.returning();
await tx.insert(posts).values({
id: crypto.randomUUID(),
title: "Min første post",
authorId: user.id,
});
});
Prepared statements
For queries som kjøres ofte, bruk prepared statements:
const getUserByEmail = db
.select()
.from(users)
.where(eq(users.email, sql.placeholder("email")))
.prepare("get_user_by_email");
// Bruk
const user = await getUserByEmail.execute({ email: "test@test.com" });
Migrasjoner
Drizzle Kit håndterer migrasjoner automatisk:
# Generer migrasjon basert på skjema-endringer
npm run db:generate
# Kjør migrasjoner
npm run db:migrate
# Åpne Drizzle Studio for visuell database-utforsking
npm run db:studio
Drizzle Studio gir deg et GUI for å utforske og redigere data. Perfekt for debugging og dataadministrasjon.
Integrasjon med Better Auth
Drizzle fungerer sømløst med Better Auth:
import { betterAuth } from "better-auth";
import { drizzleAdapter } from "better-auth/adapters/drizzle";
import { db } from "@/lib/db";
export const auth = betterAuth({
database: drizzleAdapter(db),
// Better Auth vil automatisk bruke Drizzle for auth-tabeller
});
Ytelsestips
1. Bruk select for spesifikke kolonner
// Hent kun det du trenger
const userEmails = await db
.select({ email: users.email })
.from(users);
2. Indekser
Definer indekser direkte i skjemaet:
import { pgTable, text, index } from "drizzle-orm/pg-core";
export const posts = pgTable("posts", {
id: text("id").primaryKey(),
title: text("title").notNull(),
authorId: text("author_id").notNull(),
}, (table) => ({
authorIdx: index("posts_author_idx").on(table.authorId),
}));
3. Limit og offset
Alltid bruk paginering for store datasett:
const posts = await db.query.posts.findMany({
limit: 20,
offset: 0,
orderBy: desc(posts.createdAt),
});
Hvorfor dette passer AI-først utvikling
Som vi forklarer i AI-først utvikling, handler moderne utvikling om å gjøre kode forståelig for AI-agenter.
Drizzle passer perfekt:
- Eksplisitt skjema — Claude Code kan lese
schema.tsog forstå hele datastrukturen - Type-sikkerhet — AI-en kan generere korrekt kode uten å gjette
- Konsistente mønstre — Ett mønster for alle database-operasjoner
- SQL-nært — Ingen magiske abstraksjoner å misforstå
Konklusjon
Drizzle ORM representerer fremtiden for database-utvikling i TypeScript. Det gir deg kraften til rå SQL med sikkerheten til full type-checking.
Kombinert med Neon PostgreSQL og Better Auth, har du en database-stack som er både kraftig og utviklervennlig.
LLM-First Boilerplate gir deg alt dette ferdig konfigurert, slik at du kan fokusere på å bygge features.
Les mer
- Kom i gang med LLM-First Boilerplate — Komplett oppsett inkludert database
- Better Auth vs NextAuth — Autentisering som fungerer med Drizzle
- LLM-First vs ShipFast — Hvorfor vi valgte Drizzle over MongoDB
Kilder
- Drizzle ORM dokumentasjon — Offisiell dokumentasjon
- Drizzle ORM GitHub — Kildekode og issues
- Neon PostgreSQL — Serverless PostgreSQL
- PostgreSQL Identity Columns — PostgreSQL dokumentasjon
Vil du se Drizzle i aksjon? Start med LLM-First Boilerplate
og utforsk database-skjemaet i lib/db/schema.ts.