Johdanto
Tietokantaintegraatio on jokaisen full-stack-sovelluksen sydän. Ilman sitä sovelluksesi on käytännössä pelkkä staattinen sivu. Next.js App Routerin myötä perinteiset tavat käsitellä dataa ovat kuitenkin muuttuneet aika paljon — ja rehellisesti sanottuna, se on hyvä asia.
Vuonna 2026 Drizzle ORM on noussut yhdeksi suosituimmista TypeScript-ORM-kirjastoista, erityisesti Next.js-kehittäjien keskuudessa. Yli 25 000 GitHub-tähteä ja nolla riippuvuutta. Se on kevyt, nopea ja suunniteltu alusta asti serverless- ja edge-ympäristöihin.
Tässä oppaassa rakennetaan tietokantaintegraatio Next.js App Routeriin Drizzle ORM:n avulla ihan alusta loppuun. Käydään läpi projektin pystytys, skeeman määrittely, CRUD-operaatiot Server Componenteilla ja Server Actioneilla, yhteyspoolin hallinta ja käyttöönotto Vercelissä Neon Postgres -tietokannalla. Kaikki käytännön koodiesimerkein — ei pelkkää teoriaa.
Miksi Drizzle ORM vuonna 2026?
Ennen kuin sukellomme teknisiin yksityiskohtiin, pysähdytään hetkeksi. Miksi juuri Drizzle? Prismahan on ollut Next.js-ekosysteemin hallitseva ORM jo vuosia. Vastaus kiteytyy kolmeen asiaan.
SQL-läheinen lähestymistapa
Monet ORM-kirjastot pyrkivät abstrahoimaan SQL:n kokonaan pois. Drizzle tekee päinvastoin — se omaksuu SQL:n ja tarjoaa TypeScript-rajapinnan, joka peilaa tuttua SQL-syntaksia.
Jos osaat SQL:ää, osaat Drizzlen. Näin yksinkertaista se on. Oppimiskäyrä on käytännössä olematon.
Kevyt ja nopea
Drizzlen runtime-koko on noin 7,4 KB minifoituna ja gzip-pakattuna — nolla riippuvuutta. Vertailun vuoksi: Prisman uusimmassa versiossa 7 query engine on noin 1,6 MB (gzip-pakattuna 600 KB), kun se aiemmin oli jopa 14 MB. Drizzle on siis kertaluokkaa pienempi, ja se näkyy ihan suoraan kylmäkäynnistysajoissa serverless-ympäristöissä.
Natiivi edge-tuki
Drizzle toimii suoraan Vercelin Edge Runtimessa, Cloudflare Workersissa ja muissa edge-ympäristöissä ilman mitään lisäkonfiguraatiota. Prisma vaatii tähän erillisen Prisma Accelerate -palvelun. Drizzle tukee myös natiivisti serverless-tietokantoja kuten Neon, Turso, PlanetScale ja Cloudflare D1.
Drizzle vs. Prisma: nopea vertailu
Tässä tärkeimmät erot tiivistetysti:
- Pakettikoko: Drizzle ~7,4 KB vs. Prisma ~1,6 MB (gzip)
- Kylmäkäynnistys: Drizzle ~400 ms vs. Prisma ~1100 ms (Vercel Functions)
- Edge Runtime: Drizzle natiivisti vs. Prisma vaatii Acceleraten
- Migraatiot: Drizzle TypeScript-pohjainen vs. Prisma automaattinen CLI
- SQL-kontrolli: Drizzle SQL-läheinen vs. Prisma abstrakti API
- TypeScript-tyyppitarkistus: Prisma nopeampi suurissa skeemoissa
Kumpikin on erinomainen työkalu — valinta riippuu aina projektin tarpeista. Tässä oppaassa keskitytään Drizzleen, koska se sopii erityisen hyvin Next.js App Routerin serverless-arkkitehtuuriin.
Projektin pystytys
Eli, aloitetaan käytännön työstä. Luodaan uusi Next.js-projekti ja asennetaan tarvittavat paketit. Käytetään PostgreSQL-tietokantaa Neon-palvelun kautta, koska se sopii erinomaisesti serverless-ympäristöön.
Next.js-projektin luominen
npx create-next-app@latest my-drizzle-app
# Valitse TypeScript, Tailwind CSS ja App Router
cd my-drizzle-app
Drizzle ORM:n ja riippuvuuksien asentaminen
# Drizzle ORM ja PostgreSQL-ajuri
npm install drizzle-orm @neondatabase/serverless
# Drizzle Kit kehitystyökaluksi
npm install -D drizzle-kit
drizzle-orm on itse ORM-kirjasto, joka tarjoaa kyselyrajapinnan ja tyyppityökalut. @neondatabase/serverless on Neonin serverless-ajuri, joka mahdollistaa tietokantayhteyden edge-ympäristöissä HTTP:n tai WebSocketien kautta perinteisen TCP:n sijaan. Ja drizzle-kit on kehitystyökalu migraatioiden generointiin ja skeeman hallintaan — tätä tarvitset vain kehitysvaiheessa.
Ympäristömuuttujien määrittely
Luo .env-tiedosto projektin juureen. Saat yhteysmerkkijonon Neon-hallintapaneelista projektin luomisen jälkeen:
POSTGRES_URL="postgres://[kayttaja]:[salasana]@[host]-[alue].aws.neon.tech:5432/[tietokanta]?sslmode=require"
Projektin kansiorakenne
Järjestetään tietokantaan liittyvät tiedostot selkeästi. Itse tykkään käyttää tätä rakennetta:
my-drizzle-app/
├── app/
│ ├── page.tsx
│ ├── actions/
│ │ └── posts.ts # Server Actions
│ └── components/
│ └── post-form.tsx # Client Components
├── db/
│ ├── schema.ts # Tietokantaskeema
│ ├── index.ts # Tietokantayhteys
│ └── migrations/ # Migraatiotiedostot
├── drizzle.config.ts # Drizzle Kit -konfiguraatio
└── .env
Drizzle-konfiguraatio ja tietokantayhteys
drizzle.config.ts
Drizzle Kit käyttää tätä konfiguraatiotiedostoa migraatioiden generointiin ja tietokantayhteyden muodostamiseen:
import { defineConfig } from "drizzle-kit";
export default defineConfig({
schema: "./db/schema.ts",
out: "./db/migrations",
dialect: "postgresql",
dbCredentials: {
url: process.env.POSTGRES_URL!,
},
});
Tietokantayhteyden muodostaminen
Luodaan db/index.ts-tiedosto, jossa muodostetaan Drizzle-yhteys Neonin serverless-ajurilla:
import { drizzle } from "drizzle-orm/neon-serverless";
import * as schema from "./schema";
export const db = drizzle(process.env.POSTGRES_URL!, {
schema,
});
Ja siinä se. Vakavasti — tämä on Drizzlen vahvuus. Yhteyden muodostaminen vaatii vain muutaman rivin koodia. Neonin serverless-ajuri huolehtii automaattisesti yhteyspoolin hallinnasta ja toimii sekä Node.js- että edge-ympäristöissä.
Jos käytät perinteistä PostgreSQL-palvelinta (esim. omalla palvelimella), käytä node-postgres-ajuria yhteyspoolin kanssa:
import { drizzle } from "drizzle-orm/node-postgres";
import { Pool } from "pg";
import * as schema from "./schema";
const pool = new Pool({
connectionString: process.env.POSTGRES_URL!,
max: 10, // Maksimi yhtäaikaisten yhteyksien määrä
});
export const db = drizzle(pool, { schema });
Skeeman määrittely TypeScriptillä
Tässä kohtaa Drizzle alkaa todella loistaa. Skeema määritellään suoraan TypeScriptillä — ei erillistä DSL-kieltä kuten Prismassa. Tämä tarkoittaa, että skeema ja tyypit pysyvät automaattisesti synkronissa. Ei enää sitä tilannetta, että tyypit ja tietokanta ovat ristiriidassa keskenään.
Perustaulut
Luodaan db/schema.ts-tiedostoon yksinkertainen blogialusta esimerkiksi:
import {
pgTable,
uuid,
text,
timestamp,
boolean,
integer,
} from "drizzle-orm/pg-core";
import { relations } from "drizzle-orm";
// Käyttäjätaulu
export const users = pgTable("users", {
id: uuid("id").primaryKey().defaultRandom(),
name: text("name").notNull(),
email: text("email").notNull().unique(),
avatarUrl: text("avatar_url"),
createdAt: timestamp("created_at").defaultNow().notNull(),
});
// Artikkelitaulu
export const posts = pgTable("posts", {
id: uuid("id").primaryKey().defaultRandom(),
title: text("title").notNull(),
content: text("content").notNull(),
published: boolean("published").default(false).notNull(),
authorId: uuid("author_id")
.references(() => users.id, { onDelete: "cascade" })
.notNull(),
viewCount: integer("view_count").default(0).notNull(),
createdAt: timestamp("created_at").defaultNow().notNull(),
updatedAt: timestamp("updated_at").defaultNow().notNull(),
});
// Kommenttitaulu
export const comments = pgTable("comments", {
id: uuid("id").primaryKey().defaultRandom(),
content: text("content").notNull(),
postId: uuid("post_id")
.references(() => posts.id, { onDelete: "cascade" })
.notNull(),
authorId: uuid("author_id")
.references(() => users.id, { onDelete: "cascade" })
.notNull(),
createdAt: timestamp("created_at").defaultNow().notNull(),
});
Relaatioiden määrittely
Drizzle tukee relaatioiden määrittelyä, jonka avulla voit hakea liittyvää dataa ilman manuaalisia JOIN-operaatioita. Tämä on yllättävän kätevää käytännössä:
// Relaatiot (samassa schema.ts-tiedostossa)
export const usersRelations = relations(users, ({ many }) => ({
posts: many(posts),
comments: many(comments),
}));
export const postsRelations = relations(posts, ({ one, many }) => ({
author: one(users, {
fields: [posts.authorId],
references: [users.id],
}),
comments: many(comments),
}));
export const commentsRelations = relations(comments, ({ one }) => ({
post: one(posts, {
fields: [comments.postId],
references: [posts.id],
}),
author: one(users, {
fields: [comments.authorId],
references: [users.id],
}),
}));
Migraatioiden generointi ja ajaminen
Kun skeema on valmis, generoidaan migraatiotiedostot ja sovelletaan ne tietokantaan:
# Generoi migraatiotiedostot skeeman perusteella
npx drizzle-kit generate
# Sovella migraatiot tietokantaan
npx drizzle-kit migrate
# Tai vaihtoehtoisesti: työnnä skeema suoraan (kehityksessä)
npx drizzle-kit push
generate luo SQL-migraatiotiedostot db/migrations/-kansioon vertaamalla nykyistä skeemaa edelliseen. migrate ajaa nämä tiedostot tietokantaan. push taas on kätevä kehitysvaiheessa — se synkronoi skeeman suoraan ilman migraatiotiedostoja.
Yksi tärkeä huomio: muista tallentaa migraatiotiedostot versionhallintaan. Ne ovat olennainen osa sovelluksen historiaa ja mahdollistavat turvallisen käyttöönoton tuotannossa.
CRUD-operaatiot Server Componenteilla
Nyt päästään siihen hauskaan osaan. Next.js App Routerissa jokainen komponentti on oletuksena Server Component, mikä tarkoittaa, että voit tehdä tietokantakyselyitä suoraan komponenteissa — ilman erillisiä API-reittejä. Jos tämä kuulostaa oudolta, se on aluksi sitä kaikille.
Datan hakeminen Server Componentissa
// app/page.tsx
import { db } from "@/db";
import { posts, users } from "@/db/schema";
import { desc, eq } from "drizzle-orm";
export default async function HomePage() {
// Hae julkaistut artikkelit uusimmasta vanhimpaan
const publishedPosts = await db
.select({
id: posts.id,
title: posts.title,
content: posts.content,
viewCount: posts.viewCount,
createdAt: posts.createdAt,
authorName: users.name,
})
.from(posts)
.innerJoin(users, eq(posts.authorId, users.id))
.where(eq(posts.published, true))
.orderBy(desc(posts.createdAt))
.limit(10);
return (
<main>
<h1>Blogi</h1>
{publishedPosts.map((post) => (
<article key={post.id}>
<h2>{post.title}</h2>
<p>Kirjoittaja: {post.authorName}</p>
<p>{post.content.substring(0, 200)}...</p>
<span>{post.viewCount} katselua</span>
</article>
))}
</main>
);
}
Huomaa miten SQL-läheinen syntaksi on: select(), from(), innerJoin(), where(), orderBy() ja limit() vastaavat suoraan SQL-operaatioita. Jos olet kirjoittanut SQL:ää aiemmin, tämä tuntuu heti kotoisalta.
Relaatiokyselyt Drizzle Query API:lla
SQL-tyylisen rajapinnan lisäksi Drizzle tarjoaa myös Query API:n, joka palauttaa liittyvät taulut automaattisesti sisäkkäisinä objekteina. Tämä on oma suosikkini silloin, kun tarvitsen useita tasoja relaatioita:
// app/posts/[id]/page.tsx
import { db } from "@/db";
import { eq } from "drizzle-orm";
import { notFound } from "next/navigation";
export default async function PostPage({
params,
}: {
params: Promise<{ id: string }>;
}) {
const { id } = await params;
const post = await db.query.posts.findFirst({
where: (posts, { eq }) => eq(posts.id, id),
with: {
author: true,
comments: {
with: {
author: true,
},
orderBy: (comments, { desc }) => [desc(comments.createdAt)],
},
},
});
if (!post) notFound();
return (
<article>
<h1>{post.title}</h1>
<p>Kirjoittaja: {post.author.name}</p>
<div>{post.content}</div>
<h2>Kommentit ({post.comments.length})</h2>
{post.comments.map((comment) => (
<div key={comment.id}>
<strong>{comment.author.name}</strong>
<p>{comment.content}</p>
</div>
))}
</article>
);
}
Query API:n with-kenttä hakee liittyvät taulut automaattisesti — ei tarvitse kirjoittaa JOIN-lausekkeita käsin. Todella näppärää.
Datan muokkaus Server Actioneilla
Server Actionit ovat Next.js App Routerin tapa käsitellä lomakkeita ja mutaatioita palvelinpuolella. Ne toimivat saumattomasti Drizzlen kanssa, ja tämä yhdistelmä on rehellisesti sanottuna aika elegantti.
CRUD-operaatiot Server Actioneina
// app/actions/posts.ts
"use server";
import { db } from "@/db";
import { posts } from "@/db/schema";
import { eq } from "drizzle-orm";
import { revalidatePath } from "next/cache";
// CREATE — Uuden artikkelin luominen
export async function createPost(formData: FormData) {
const title = formData.get("title") as string;
const content = formData.get("content") as string;
const authorId = formData.get("authorId") as string;
const [newPost] = await db
.insert(posts)
.values({
title,
content,
authorId,
})
.returning();
revalidatePath("/");
return newPost;
}
// UPDATE — Artikkelin päivittäminen
export async function updatePost(postId: string, formData: FormData) {
const title = formData.get("title") as string;
const content = formData.get("content") as string;
const [updated] = await db
.update(posts)
.set({
title,
content,
updatedAt: new Date(),
})
.where(eq(posts.id, postId))
.returning();
revalidatePath(`/posts/${postId}`);
return updated;
}
// DELETE — Artikkelin poistaminen
export async function deletePost(postId: string) {
await db.delete(posts).where(eq(posts.id, postId));
revalidatePath("/");
}
// PUBLISH — Artikkelin julkaiseminen
export async function togglePublish(postId: string) {
const post = await db.query.posts.findFirst({
where: (posts, { eq }) => eq(posts.id, postId),
});
if (!post) throw new Error("Artikkelia ei löydy");
const [updated] = await db
.update(posts)
.set({ published: !post.published })
.where(eq(posts.id, postId))
.returning();
revalidatePath("/");
return updated;
}
Muutama asia kannattaa huomata. .returning() on olennainen — ilman sitä insert- ja update-operaatiot onnistuvat mutta eivät palauta mitään dataa. revalidatePath() varmistaa, että välimuistissa oleva sivu päivittyy operaation jälkeen. Ja käytä aina .where()-lauseketta update- ja delete-operaatioissa — ilman sitä operaatio kohdistuu kaikkiin riveihin (ja se on aika ikävä yllätys tuotannossa).
Lomakkeen käyttö Client Componentissa
// app/components/post-form.tsx
"use client";
import { createPost } from "@/app/actions/posts";
import { useActionState } from "react";
export function PostForm({ authorId }: { authorId: string }) {
const [state, formAction, isPending] = useActionState(
async (_prev: unknown, formData: FormData) => {
formData.set("authorId", authorId);
return await createPost(formData);
},
null
);
return (
<form action={formAction}>
<div>
<label htmlFor="title">Otsikko</label>
<input
id="title"
name="title"
type="text"
required
/>
</div>
<div>
<label htmlFor="content">Sisältö</label>
<textarea
id="content"
name="content"
rows={6}
required
/>
</div>
<button type="submit" disabled={isPending}>
{isPending ? "Tallennetaan..." : "Julkaise artikkeli"}
</button>
</form>
);
}
Tärkeä yksityiskohta: Drizzlen db-yhteys toimii ainoastaan palvelinpuolella — Server Componenteissa, Server Actioneissa ja API-reiteissä. Jos yrität käyttää sitä Client Componentissa, saat virheen. Ratkaisu on juuri tämä: määrittele mutaatiot Server Actioneina ja kutsu niitä Client Componentista.
Syötteiden validointi Drizzle-Zodilla
Tuotantosovelluksessa et voi koskaan luottaa siihen, että käyttäjä syöttää oikeanlaista dataa. Drizzle tarjoaa integroitua tukea Zod-validoinnille, joka generoi validointiskeemoja suoraan tietokantaskeemasta:
// db/validations.ts
import { createInsertSchema, createSelectSchema } from "drizzle-orm/zod";
import { posts } from "./schema";
// Generoi Zod-skeema insert-operaatiolle
export const insertPostSchema = createInsertSchema(posts, {
title: (schema) => schema.min(3, "Otsikon on oltava vähintään 3 merkkiä"),
content: (schema) => schema.min(10, "Sisällön on oltava vähintään 10 merkkiä"),
});
// Generoi Zod-skeema select-operaatiolle
export const selectPostSchema = createSelectSchema(posts);
// app/actions/posts.ts — päivitetty versio validoinnilla
"use server";
import { db } from "@/db";
import { posts } from "@/db/schema";
import { insertPostSchema } from "@/db/validations";
import { revalidatePath } from "next/cache";
export async function createPost(formData: FormData) {
const raw = {
title: formData.get("title") as string,
content: formData.get("content") as string,
authorId: formData.get("authorId") as string,
};
// Validoi syötteet ennen tietokantaoperaatiota
const validated = insertPostSchema.parse(raw);
const [newPost] = await db
.insert(posts)
.values(validated)
.returning();
revalidatePath("/");
return newPost;
}
Tämä on erityisen hyödyllistä siksi, että validointiskeema pysyy automaattisesti synkronissa tietokantaskeeman kanssa. Jos muutat taulurakennetta, validointi päivittyy samalla. Yksi huoli vähemmän.
Edistyneet kyselyt ja suorituskyvyn optimointi
Sivutus ja suodatus
Suurten tietoaineistojen käsittelyssä sivutus on välttämätöntä. Drizzle tukee limit()- ja offset()-metodeja, ja voit ajaa datakyselyt ja rivimäärälaskennan rinnakkain Promise.all()-kutsulla. Tämä tekee iso eron sivun latausaikaan:
import { db } from "@/db";
import { posts } from "@/db/schema";
import { count, desc, ilike, eq, and } from "drizzle-orm";
interface PostsQuery {
page?: number;
pageSize?: number;
search?: string;
}
export async function getPaginatedPosts({
page = 1,
pageSize = 10,
search,
}: PostsQuery) {
const offset = (page - 1) * pageSize;
// Rakennetaan suodatusehto dynaamisesti
const conditions = [eq(posts.published, true)];
if (search) {
conditions.push(ilike(posts.title, `%${search}%`));
}
// Aja molemmat kyselyt rinnakkain
const [data, [{ total }]] = await Promise.all([
db
.select()
.from(posts)
.where(and(...conditions))
.orderBy(desc(posts.createdAt))
.limit(pageSize)
.offset(offset),
db
.select({ total: count() })
.from(posts)
.where(and(...conditions)),
]);
return {
posts: data,
totalPages: Math.ceil(total / pageSize),
currentPage: page,
};
}
Raaka-SQL tarvittaessa
Joskus Drizzlen kyselyrakentaja ei yksinkertaisesti riitä — esimerkiksi monimutkaisten aggregaatioiden tai tietokantakohtaisten ominaisuuksien kohdalla. Silloin voit käyttää sql-template-tagia. Muuttujat parametrisoidaan automaattisesti, joten SQL-injektioilta on suojattu:
import { db } from "@/db";
import { posts } from "@/db/schema";
import { sql } from "drizzle-orm";
export async function getPostStats() {
const result = await db
.select({
month: sql<string>`TO_CHAR(${posts.createdAt}, 'YYYY-MM')`,
postCount: sql<number>`COUNT(*)::int`,
totalViews: sql<number>`SUM(${posts.viewCount})::int`,
})
.from(posts)
.where(sql`${posts.published} = true`)
.groupBy(sql`TO_CHAR(${posts.createdAt}, 'YYYY-MM')`)
.orderBy(sql`TO_CHAR(${posts.createdAt}, 'YYYY-MM') DESC`);
return result;
}
Verceliin käyttöönotto Neon Postgres -tietokannalla
Drizzle ORM ja Neon Postgres muodostavat erinomaisen parin Vercel-käyttöönottoon. Neon tarjoaa serverless-PostgreSQL:n, joka skaalautuu automaattisesti ja tukee edge-yhteyksiä. Kokemukseni perusteella tämä on yksi sujuvimmista tavoista saada tietokanta käyttöön.
Vaihe 1: Neon-tietokannan luominen
Luo tietokanta Neon-palvelussa (neon.tech) ja kopioi yhteysmerkkijono. Neon tarjoaa ilmaisen tason, joka riittää hyvin kehitykseen ja pieniin tuotantosovelluksiin.
Vaihe 2: Ympäristömuuttujat Vercelissä
Lisää POSTGRES_URL-ympäristömuuttuja Vercelin projektin asetuksissa. Voit tehdä tämän hallintapaneelista tai CLI:llä:
vercel env add POSTGRES_URL
Vaihe 3: Migraatiot tuotannossa
Lisää migraatiokomento package.json-tiedostoon. Itse suosin tapaa, jossa migraatiot ajetaan osana build-prosessia:
{
"scripts": {
"db:generate": "drizzle-kit generate",
"db:migrate": "drizzle-kit migrate",
"db:push": "drizzle-kit push",
"db:studio": "drizzle-kit studio",
"build": "npm run db:migrate && next build"
}
}
Nyt migraatiot ajetaan automaattisesti jokaisen käyttöönoton yhteydessä. Tuotantoympäristössä kannattaa aina käyttää migrate-komentoa push-komennon sijaan — se on turvallisempi ja jäljitettävämpi.
Drizzle Studio: visuaalinen tietokantahallinta
Drizzle tarjoaa myös visuaalisen hallintatyökalun tietokannan tarkasteluun:
npx drizzle-kit studio
Drizzle Studio avautuu osoitteeseen https://local.drizzle.studio ja tarjoaa intuitiivisen käyttöliittymän tietokannan sisällön selaamiseen, muokkaamiseen ja kyselyiden testaamiseen. Todella hyödyllinen työkalu kehitysvaiheessa.
Yleisimmät sudenkuopat ja niiden välttäminen
Nämä ovat ne virheet, joihin melkein jokainen kompastuu jossain vaiheessa Drizzle + Next.js -projektia:
- db-yhteyden käyttö Client Componentissa: Drizzlen tietokantayhteys toimii ainoastaan palvelinpuolella. Käytä Server Actioneja tai API-reittejä datan mutaatioihin.
- Välimuistin päivittäminen unohtuu: Käytä aina
revalidatePath()tairevalidateTag()mutaatioiden jälkeen. Muuten käyttöliittymässä näkyy vanhentunut data — ja tulet ihmettelemään miksi muutokset eivät näy. - Peräkkäiset kyselyt rinnakkaisten sijaan: Käytä
Promise.all()aina, kun kyselyt eivät riipu toisistaan. Tämä voi puolittaa sivun latausajan. - Migraatiotiedostojen jättäminen pois versionhallinnasta: Tallenna aina migraatiot Gitiin. Ne ovat ainoa luotettava tapa jäljittää tietokantamuutoksia.
- WHERE-lausekkeen unohtaminen update/delete-operaatioissa: Ilman
where()-ehtoa operaatio kohdistuu kaikkiin taulun riveihin. Tämä voi pilata koko tietokannan yhdellä komennolla.
Usein kysytyt kysymykset
Voiko Drizzle ORM:ää käyttää Next.js:n Edge Runtimessa?
Kyllä, ja tämä on yksi sen vahvuuksista. Drizzle toimii natiivisti Edge Runtimessa ilman lisäkonfiguraatiota. Sen pieni pakettikoko (7,4 KB) ja nolla riippuvuutta tekevät siitä ihanteellisen edge-ympäristöihin. Tarvitset kuitenkin edge-yhteensopivan tietokanta-ajurin, kuten Neonin serverless-ajurin tai Turson HTTP-ajurin.
Tarvitseeko Drizzle ORM erillistä API-kerrosta Next.js App Routerissa?
Ei. Server Componenteissa voit tehdä tietokantakyselyjä suoraan komponentissa, ja mutaatiot hoidetaan Server Actioneilla. Erilliset API-reitit ovat silti tarpeellisia, jos tarvitset REST- tai GraphQL-rajapinnan ulkoisille asiakkaille — mutta sisäiseen käyttöön ne ovat yleensä turhia.
Miten Drizzle ORM:n suorituskyky vertautuu Prismaan?
Drizzle on merkittävästi nopeampi kylmäkäynnistyksissä (noin 400 ms vs. 1100 ms Vercel Functionsissa) ja tuottaa pienemmän paketin. Prisma 7 on kuitenkin kaventanut eroa huomattavasti uudella TypeScript/WebAssembly-pohjaisella query enginellä. Tyypillisessä web-sovelluksessa ero tasoittuu, mutta serverless- ja edge-ympäristöissä Drizzle on edelleen nopeampi.
Tukeeko Drizzle ORM MongoDB:tä tai muita NoSQL-tietokantoja?
Ei. Drizzle on suunniteltu nimenomaan SQL-tietokannoille: PostgreSQL, MySQL, SQLite ja SingleStore. Jos tarvitset MongoDB-tukea, Prisma on parempi vaihtoehto tällä hetkellä. Drizzle tukee kuitenkin laajaa valikoimaa serverless-SQL-tietokantoja kuten Neon, Turso, PlanetScale ja Cloudflare D1.
Miten Drizzle ORM:n migraatiot eroavat Prisman migraatioista?
Drizzlen migraatiot generoidaan drizzle-kit generate -komennolla ja ne ovat puhtaita SQL-tiedostoja, joita voit tarkistaa ja muokata ennen ajamista. Prisman migraatiot ovat automaattisempia — prisma migrate generoi ja ajaa ne yhdellä komennolla. Drizzlen lähestymistapa antaa enemmän kontrollia, mutta vaatii hieman enemmän manuaalista työtä. Omasta mielestäni tämä lisäkontrolli on sen arvoista tuotantoprojekteissa.
Yhteenveto
Drizzle ORM on erinomainen valinta Next.js App Router -projekteihin vuonna 2026. Sen SQL-läheinen syntaksi, minimaalinen pakettikoko ja natiivi edge-tuki tekevät siitä ihanteellisen moderniin serverless-kehitykseen.
Yhdistettynä Server Componentteihin ja Server Actioneihin saat aikaan tehokkaan full-stack-arkkitehtuurin, jossa data kulkee suoraan tietokannasta käyttöliittymään ilman turhia välitasoja. Se on nopea, tyyppiturvallinen ja yllättävän miellyttävä käyttää.
Tässä oppaassa käsiteltiin projektin pystytys, skeeman määrittely, CRUD-operaatiot, validointi ja käyttöönotto. Seuraava luonnollinen askel on tutustua Drizzlen edistyneisiin ominaisuuksiin kuten transaktioihin, prepared statementeihin ja monitorointiin tuotantoympäristössä.