Back

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.

Ronny Bruknapp
Ronny Bruknapp
December 22, 2025
Updated Dec 22, 2025
Share:

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:

  1. Eksplisitt skjemaClaude Code kan lese schema.ts og forstå hele datastrukturen
  2. Type-sikkerhet — AI-en kan generere korrekt kode uten å gjette
  3. Konsistente mønstre — Ett mønster for alle database-operasjoner
  4. 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

Kilder

Vil du se Drizzle i aksjon? Start med LLM-First Boilerplate og utforsk database-skjemaet i lib/db/schema.ts.