Skip to main content
Back to Blog
WebTechnology

A Spec-Driven Next.js Setup

Jan 2026Awais Niaz
A Spec-Driven Next.js Setup

The gap between a written spec and working code is where most projects derail. A spec-driven setup closes that gap by making the spec executable — types, data layer, and routes all derive from a single source of truth.

Start with a Schema

Before writing a single component, define your data models. In a Next.js project this means:

  1. Zod schemas for runtime validation
  2. TypeScript types inferred from those schemas
  3. Prisma or Drizzle models that mirror the same shape

When all three come from one definition, a change to the spec propagates everywhere. The compiler catches drift before it reaches production.

Route Design from the Spec

Your routes are your API contract. Define them upfront as a typed map:

export const routes = {
  blog: {
    list:    "/api/blog" as const,
    bySlug:  (slug: string) => `/api/blog/${slug}` as const,
  },
} as const;

Every page and API handler references this map. No magic strings. No guessing what a route returns.

The Data Layer

Use server components for data fetching. Next.js App Router makes this natural — the component asks for data, the server delivers it, and the types match because they share the same schema.

Keep data fetching in dedicated files, not scattered across components. A src/lib/queries.ts file that exports typed fetchers means any page can get its data in one line.

Keeping Routes and Types in Sync

The biggest maintenance win is automated type generation. When your Prisma schema changes, regenerate the types. When your Zod schemas update, the API response types update. CI should fail if a route returns data that doesn't match its declared shape.

Summary

A spec-driven setup is not slower — it's faster over the life of a project. The upfront investment in types and schemas pays back the first time you refactor without breaking anything.