You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Next.js API路由中为请求体参数添加TypeScript类型?

How to Add Type Safety to Next.js API Request Bodies (Frontend + Backend)

Great question! Type safety for Next.js API routes is definitely achievable—we just need to add explicit type definitions for request bodies both on the backend (API route) and frontend (fetch calls). Here's a step-by-step solution to fix your issue:


Backend: Type-Safe API Route

First, let's fix the API route so TypeScript recognizes the structure of req.body and validates it at runtime (since TypeScript types disappear after compilation).

1. Define a Request Body Type

Start by creating a TypeScript type for your request body:

type AddRequestBody = {
  number_one: number;
  number_two: number;
};

2. Add a Runtime Type Guard

To ensure the incoming request actually matches your type (since frontend could send invalid data), use a type guard function. This tells TypeScript to narrow the type of req.body once validation passes:

function isValidAddRequestBody(body: unknown): body is AddRequestBody {
  return (
    typeof body === 'object' &&
    body !== null &&
    'number_one' in body &&
    typeof (body as AddRequestBody).number_one === 'number' &&
    'number_two' in body &&
    typeof (body as AddRequestBody).number_two === 'number'
  );
}

3. Update Your API Route

Use the type guard in your API route to validate the request body before processing it:

import type { NextApiRequest, NextApiResponse } from 'next';

type AddRequestBody = {
  number_one: number;
  number_two: number;
};

const add = (a: number, b: number): number => a + b;

function isValidAddRequestBody(body: unknown): body is AddRequestBody {
  return (
    typeof body === 'object' &&
    body !== null &&
    'number_one' in body &&
    typeof (body as AddRequestBody).number_one === 'number' &&
    'number_two' in body &&
    typeof (body as AddRequestBody).number_two === 'number'
  );
}

export default async (req: NextApiRequest, res: NextApiResponse) => {
  try {
    // Validate request body first
    if (!isValidAddRequestBody(req.body)) {
      return res.status(400).json({ err: 'Invalid request: number_one and number_two must be numbers' });
    }

    // Now TypeScript knows req.body is AddRequestBody—no more `any`!
    const result = add(req.body.number_one, req.body.number_two);
    res.status(200).json(result);
  } catch (err) {
    res.status(500).json({ err: 'Server error' });
  }
};

Frontend: Type-Safe Fetch Calls

Next, let's ensure your frontend fetch calls enforce the same type constraints, so TypeScript catches invalid parameters before they're sent.

1. Reuse the Request Body Type

You can reuse the same AddRequestBody type (consider moving it to a shared types folder for DRY code):

// Import or define the type here
type AddRequestBody = {
  number_one: number;
  number_two: number;
};

2. Create a Type-Safe Fetch Wrapper

Wrap your fetch call in a function that accepts only AddRequestBody as the body parameter:

async function callAddApi(body: AddRequestBody) {
  const response = await fetch("/api/add", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify(body),
  });
  
  if (!response.ok) {
    throw new Error('API request failed');
  }
  
  return response.json();
}

3. Test Type Enforcement

Now TypeScript will throw an error if you pass invalid data:

// ✅ Valid call (no TypeScript errors)
callAddApi({ number_one: 1, number_two: 2 });

// ❌ Invalid call (TypeScript will highlight the error: "Type 'string' is not assignable to type 'number'")
callAddApi({ number_one: 1, number_two: "two" });

Optional: Simplify with Zod (For Shared Validation)

If you want to avoid writing manual type guards and share validation logic between frontend and backend, use Zod—a popular library for schema declaration and validation.

1. Define a Zod Schema

import { z } from 'zod';

// Define schema (generates TypeScript type automatically)
const AddRequestBodySchema = z.object({
  number_one: z.number(),
  number_two: z.number(),
});

// Infer TypeScript type from the schema
type AddRequestBody = z.infer<typeof AddRequestBodySchema>;

2. Use the Schema in Backend

Zod handles runtime validation and type narrowing automatically:

import { z } from 'zod';
import type { NextApiRequest, NextApiResponse } from 'next';

const AddRequestBodySchema = z.object({
  number_one: z.number(),
  number_two: z.number(),
});
type AddRequestBody = z.infer<typeof AddRequestBodySchema>;

const add = (a: number, b: number): number => a + b;

export default async (req: NextApiRequest, res: NextApiResponse) => {
  try {
    // Validate and parse the request body
    const validatedBody = AddRequestBodySchema.parse(req.body);
    const result = add(validatedBody.number_one, validatedBody.number_two);
    res.status(200).json(result);
  } catch (err) {
    if (err instanceof z.ZodError) {
      // Return detailed validation errors
      return res.status(400).json({ err: 'Invalid request', details: err.errors });
    }
    res.status(500).json({ err: 'Server error' });
  }
};

3. Reuse the Schema in Frontend

You can even validate the data on the frontend before sending it:

import { z } from 'zod';

const AddRequestBodySchema = z.object({
  number_one: z.number(),
  number_two: z.number(),
});
type AddRequestBody = z.infer<typeof AddRequestBodySchema>;

async function callAddApi(body: AddRequestBody) {
  // Optional: Frontend validation (catches errors early)
  AddRequestBodySchema.parse(body);
  
  const response = await fetch("/api/add", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify(body),
  });
  
  return response.json();
}

内容的提问来源于stack exchange,提问作者Michael Gingras

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.01 02:12:32