Next.js 15 fullstack (App Router + Server Actions) · Lektion

Validering av begäranden och typade JSON-svar med Zod

Parsa och validera begärandekroppar och frågeparametrar och returnera typade, välstrukturerade felsvar.

Lektion 4 av 413 steg

Validering av begäranden och typade JSON-svar med Zod är en gratis lektion i Next.js 15 fullstack (App Router + Server Actions) på CoddyKit. Detta är lektion 4 av 4. Du kan läsa vilka 3 lektioner som helst i den här lärvägen kostnadsfritt i sin helhet – därefter låser CoddyKit PRO upp alla lektioner, plus praktisk övning med en inbyggd kodredigerare och en AI-lärare dygnet runt. Den ingår i lärvägen för Next.js 15 fullstack (App Router + Server Actions), och Era framsteg synkroniseras mellan webben och CoddyKit-appen. Kursen i Next.js 15 fullstack (App Router + Server Actions) innehåller totalt 4 lektioner.

Varför validera i Edge?

I Next.js 15 Route Handlers (app/api/.../route.ts) är request-body:n helt enkelt typad JSON utan typinformation. TypeScript-typer försvinner vid körning, så en klient kan skicka vad som helst.

Zod låter dig beskriva den förväntade strukturen en gång och få både:

  • En körningskontroll som avvisar felaktig indata.
  • En statisk typ som härleds från schemat, så att handler-koden är fullständigt typad.

I den här lektionen bygger du en typad POST-handler som validerar body och query-parametrar och sedan returnerar rena, strukturerade JSON-fel.

Definiera ett schema

Börja med att deklarera den struktur du förväntar dig. Zod-scheman fungerar som den enda källan till sanning för både validering och typer.

Använd z.infer för att härleda en TypeScript-typ från schemat. Det finns ingen duplicering: ändra schemat, så uppdateras typen automatiskt.

import { z } from 'zod';

export const CreateUserSchema = z.object({
  name: z.string().min(1, 'Name is required'),
  email: z.string().email('Invalid email address'),
  age: z.number().int().positive().optional(),
});

// Inferred type — fully typed, no duplication
export type CreateUserInput = z.infer<typeof CreateUserSchema>;

Parsa request-body:n

Inuti en Route Handler läser du JSON med await request.json(). Omslut anropet med try/catch, eftersom felaktig JSON kastar ett undantag innan Zod ens körs.

Använd schema.safeParse() i stället för parse(). safeParse kastar aldrig ett undantag — den returnerar ett resultatobjekt som du kan förgrena logiken utifrån.

import { NextRequest, NextResponse } from 'next/server';
import { CreateUserSchema } from './schema';

export async function POST(request: NextRequest) {
  let body: unknown;
  try {
    body = await request.json();
  } catch {
    return NextResponse.json(
      { error: 'Invalid JSON body' },
      { status: 400 },
    );
  }

  const result = CreateUserSchema.safeParse(body);
  // ...handle result
}

safeParse: lyckat eller misslyckat

safeParse returnerar en diskriminerad union:

  • Vid lyckat resultat: { success: true, data }, där data är det typade och validerade värdet.
  • Vid misslyckat resultat: { success: false, error }, där error är ett ZodError.

TypeScript begränsar typen efter att du har kontrollerat result.success, så result.data är bara åtkomligt i grenen för lyckat resultat.

const result = CreateUserSchema.safeParse(body);

if (!result.success) {
  return NextResponse.json(
    { error: 'Validation failed', issues: result.error.issues },
    { status: 422 },
  );
}

// result.data is now typed as CreateUserInput
const user = result.data;
console.log(user.email);

Utforma ett användbart felsvar

Det fungerar att skriva ut det råa ZodError, men resultatet blir rörigt. Ett renare API returnerar en platt mappning från fält till meddelanden. Zods error.flatten() ger dig fieldErrors och formErrors, färdiga för klienten.

Välj en konsekvent status: 400 för indata som inte kan parsas och 422 (Unprocessable Entity) för välformad JSON som inte klarar valideringen.

if (!result.success) {
  const { fieldErrors, formErrors } = result.error.flatten();
  return NextResponse.json(
    {
      error: 'Validation failed',
      fieldErrors, // { email: ['Invalid email address'] }
      formErrors,  // top-level errors
    },
    { status: 422 },
  );
}

Validera query-parametrar

Query-parametrar kommer in som strängar via request.nextUrl.searchParams. Använd z.coerce för att konvertera och validera i ett enda steg — till exempel omvandla ?page=2 till ett faktiskt tal.

Ange standardvärden med .default() så att saknade parametrar inte får handlern att misslyckas.

import { z } from 'zod';
import { NextRequest, NextResponse } from 'next/server';

const QuerySchema = z.object({
  page: z.coerce.number().int().min(1).default(1),
  limit: z.coerce.number().int().min(1).max(100).default(20),
});

export async function GET(request: NextRequest) {
  const params = Object.fromEntries(request.nextUrl.searchParams);
  const result = QuerySchema.safeParse(params);

  if (!result.success) {
    return NextResponse.json(
      { error: 'Invalid query', fieldErrors: result.error.flatten().fieldErrors },
      { status: 400 },
    );
  }

  const { page, limit } = result.data; // numbers, defaulted
  return NextResponse.json({ page, limit });
}

Typade JSON-svar

NextResponse.json() är generisk. Skicka med ett typargument för att låsa fast formen på nyttolasten vid lyckade svar, så att din handler och din klient förblir synkroniserade.

Definiera en gemensam svarstyp och återanvänd den både på servern och i frontendens fetch-anrop.

import { NextResponse } from 'next/server';

type UserResponse = {
  id: string;
  name: string;
  email: string;
};

function ok(user: UserResponse) {
  // The generic enforces the body matches UserResponse
  return NextResponse.json<UserResponse>(user, { status: 201 });
}

En återanvändbar valideringshjälpare

Det blir omständligt att upprepa parse-/förgreningslogiken i varje handler. Extrahera en liten hjälpare som validerar en body mot valfritt schema och returnerar ett typat resultat.

På så sätt kan varje Route Handler fokusera på affärslogik, medan formatet för felsvar centraliseras.

import { z } from 'zod';
import { NextResponse } from 'next/server';

export async function parseBody<T extends z.ZodTypeAny>(
  request: Request,
  schema: T,
): Promise<
  | { ok: true; data: z.infer<T> }
  | { ok: false; response: NextResponse }
> {
  let raw: unknown;
  try {
    raw = await request.json();
  } catch {
    return { ok: false, response: NextResponse.json({ error: 'Invalid JSON' }, { status: 400 }) };
  }
  const result = schema.safeParse(raw);
  if (!result.success) {
    return {
      ok: false,
      response: NextResponse.json(
        { error: 'Validation failed', fieldErrors: result.error.flatten().fieldErrors },
        { status: 422 },
      ),
    };
  }
  return { ok: true, data: result.data };
}

Använda hjälparen i en handler

När parseBody finns på plats blir en handler kort och lättläst. Validera, returnera tidigt vid fel och arbeta sedan med fullt typade data.

Det här mönstret skalar: varje endpoint följer samma struktur, så felsvaren blir konsekventa i hela ditt API.

import { NextRequest } from 'next/server';
import { CreateUserSchema } from './schema';
import { parseBody } from '@/lib/parse-body';
import { NextResponse } from 'next/server';

export async function POST(request: NextRequest) {
  const parsed = await parseBody(request, CreateUserSchema);
  if (!parsed.ok) return parsed.response;

  const { name, email } = parsed.data; // typed
  const user = { id: crypto.randomUUID(), name, email };
  return NextResponse.json(user, { status: 201 });
}

Transformera och förfina

Zod kan göra mer än att avvisa — det kan normalisera. Använd .transform() för att omforma värden (trimma, konvertera till gemener) och .refine() för regler mellan fält som en kontroll av ett enskilt fält inte kan uttrycka.

Utgångstypen återspeglar transformationerna, så efterföljande kod ser de rensade uppgifterna.

import { z } from 'zod';

const SignupSchema = z
  .object({
    email: z.string().email().transform((s) => s.toLowerCase().trim()),
    password: z.string().min(8),
    confirm: z.string(),
  })
  .refine((d) => d.password === d.confirm, {
    message: 'Passwords do not match',
    path: ['confirm'],
  });

type Signup = z.infer<typeof SignupSchema>;

Valideringslogik som du faktiskt kan köra

Skala bort Next.js, så återstår idén om ren datavalidering. Här är ett fristående TypeScript-program som återspeglar samma parse- och förgreningsflöde med Zod, utan någon server.

Det visar både en giltig och en ogiltig nyttolast och skriver ut den strukturerade felkartan.

import { z } from 'zod';

const Schema = z.object({
  name: z.string().min(1),
  email: z.string().email(),
  age: z.coerce.number().int().positive().optional(),
});

function validate(input: unknown) {
  const result = Schema.safeParse(input);
  if (!result.success) {
    return { status: 422, body: { fieldErrors: result.error.flatten().fieldErrors } };
  }
  return { status: 201, body: result.data };
}

console.log(JSON.stringify(validate({ name: 'Ada', email: 'ada@example.com', age: '30' })));
console.log(JSON.stringify(validate({ name: '', email: 'nope' })));

Snabbkontroll

Du skriver en POST Route Handler som validerar JSON-body:n med ett Zod-schema. Vilket tillvägagångssätt returnerar bäst ett typat, strukturerat felsvar utan att krascha vid ogiltiga indata?

Sammanfattning

Du har byggt en typad och validerad API-endpoint i Next.js 15:

  • Schemat först: definiera ett Zod-schema och härled typen med z.infer.
  • safeParse, inte parse: förgrena utifrån result.success så att inget kastas.
  • Strukturerade fel: använd error.flatten(); returnera 400 för ogiltig JSON och 422 för valideringsfel.
  • Frågeparametrar: konvertera strängar med z.coerce och ange standardvärden med .default().
  • Typade svar: NextResponse.json<T>() håller servern och klienten synkroniserade.
  • Återanvändning: en parseBody-hjälpare centraliserar parse-/felmönstret i alla handlers.

Resultatet är ett edge-API som är säkert vid körning och fullt typat vid kompilering.

Gratis att börja

Lär dig TypeScript med en AI-lärare – gratis

Skriv och kör riktig kod i webbläsaren, få omedelbar hjälp av en AI-lärare dygnet runt och fortsätt där du slutade – på webben eller i appen.

Kurser
22
Lektioner
88

Vanliga frågor

Är lektionen ”Validering av begäranden och typade JSON-svar med Zod” gratis?

Ja – du kan läsa vilka 3 lektioner som helst i lärvägen Next.js 15 fullstack (App Router + Server Actions), inklusive ”Validering av begäranden och typade JSON-svar med Zod”, kostnadsfritt i sin helhet här på webben. Därefter låser CoddyKit PRO upp alla lektioner, plus interaktiv övning med en inbyggd kodredigerare och en AI-lärare dygnet runt. Kursen i Next.js 15 fullstack (App Router + Server Actions) innehåller totalt 4 lektioner.

Vad lär jag mig i ”Validering av begäranden och typade JSON-svar med Zod”?

Parsa och validera begärandekroppar och frågeparametrar och returnera typade, välstrukturerade felsvar. Ni övar på Next.js 15 fullstack (App Router + Server Actions) med praktisk kod som körs direkt i webbläsaren, medan en AI-handledare som är tillgänglig dygnet runt svarar på Era frågor under lektionen.

Behöver jag någon erfarenhet för att börja lära mig Next.js 15 fullstack (App Router + Server Actions)?

Du behöver inga förkunskaper. Utbildningen i Next.js 15 fullstack (App Router + Server Actions) på CoddyKit är upplagd för allt från nybörjare till avancerade elever, så att du kan börja här eller från början och gå fram i din egen takt. Detta är lektion 4 av 4.

Hur lång tid tar lektionen ”Validering av begäranden och typade JSON-svar med Zod”?

De flesta CoddyKit-lektioner tar cirka 5–10 minuter. Varje lektion är kort och interaktiv, så att du gör stadiga framsteg och kan fortsätta precis där du slutade – på webben eller i appen.

Kan jag skriva och köra kod i den här Next.js 15 fullstack (App Router + Server Actions)-lektionen?

Ja. Varje Next.js 15 fullstack (App Router + Server Actions)-lektion innehåller en inbyggd kodredigerare, så att du kan skriva och köra riktig kod direkt i webbläsaren och få omedelbar AI-feedback – utan lokal installation.

Alla lektioner i den här kursen

  1. Utforma RESTful-routningshanterare med Web Request API
  2. Avvägningar mellan Node-runtime och Edge-runtime
  3. Strömmande svar och ReadableStream i hanterare
  4. Validering av begäranden och typade JSON-svar med Zod
← Tillbaka till Next.js 15 fullstack (App Router + Server Actions)