Building Type-Safe APIs with Next.js 15 and Drizzle ORM
Learn how to build fully type-safe API routes in Next.js 15 using Drizzle ORM, from schema definition to runtime validation.
Building Type-Safe APIs with Next.js 15 and Drizzle ORM
In the modern web development landscape, type safety isn't just a nice-to-have — it's essential. This guide walks you through building fully type-safe API routes using Next.js 15's App Router and Drizzle ORM.
Why Type Safety Matters
When your database schema, API handlers, and frontend components all share the same type definitions, entire categories of bugs simply vanish. No more undefined is not a function in production.
Setting Up Drizzle ORM
First, let's define our schema:
import { sqliteTable, text, integer } from "drizzle-orm/sqlite-core";
export const posts = sqliteTable("posts", {
id: text("id").primaryKey(),
title: text("title").notNull(),
content: text("content").notNull(),
createdAt: integer("created_at", { mode: "timestamp" }).notNull(),
});
Creating Type-Safe API Routes
With Next.js 15 App Router, we can create API routes that leverage our Drizzle types:
import { db } from "@/lib/db";
import { posts } from "@/lib/db/schema";
import { NextResponse } from "next/server";
export async function GET() {
const allPosts = await db.select().from(posts);
return NextResponse.json(allPosts);
}
The beauty here is that allPosts is fully typed — your IDE knows exactly what fields are available.
Runtime Validation
While TypeScript gives us compile-time safety, we also need runtime validation for incoming data. Here's how to add that layer:
export async function POST(request: Request) {
const body = await request.json();
if (!body.title || !body.content) {
return NextResponse.json(
{ error: "Title and content are required" },
{ status: 400 }
);
}
const newPost = await db.insert(posts).values({
id: crypto.randomUUID(),
title: body.title,
content: body.content,
createdAt: new Date(),
}).returning();
return NextResponse.json(newPost[0], { status: 201 });
}
Conclusion
Type-safe APIs aren't just about preventing bugs — they're about developer experience. When your entire stack speaks the same type language, development becomes faster and more enjoyable.