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

Next.js路由处理器错误处理的TypeScript类型问题及最佳方案咨询

Next.js 接口错误处理的最佳结构咨询

我编写了如下Next.js路由处理器:
app/api/transactions/route.ts

import { NextRequest, NextResponse } from "next/server";
import { AxiosError } from "axios";
import axios from "../axios";
import { AxiosResponse } from "axios";
import { Transaction } from "../../../types";

interface Error {
  message: string[];
  statusCode: number;
}

export async function GET(request: NextRequest) {
  try {
    const transactionsRes: AxiosResponse<Transaction[]> = await axios.get(
      `/transactions`
    );

    return NextResponse.json(transactionsRes.data);
  } catch (error) {
    const axiosError = error as AxiosError<Error>;
    return NextResponse.json(
      { error: axiosError?.response?.data.message },
      { status: axiosError?.response?.data.statusCode }
    );
  }
}

同时在MyComponent.tsx中调用该接口:

export const MyComponent = async() => {
  const raw = await fetch('api/transaction');
  const transactions: Transaction[] = await raw.json();
  if (transactions.error) {
    throw new Error(transactions.error);
  }
  ...
}

我还使用error.tsx作为错误边界捕获错误。

目前的问题是:按此方式处理错误时,需将接口响应类型定义为Transaction[] | TransactionError,该类型既混乱又不易扩展,想咨询Next.js中错误处理的最佳结构是什么?


最佳错误处理结构方案

1. 统一API响应格式

前后端约定统一的响应外层结构,无论成功失败都遵循该格式,彻底消除联合类型的困扰。

首先定义通用响应类型:

// types/api.ts
export interface ApiResponse<T = unknown> {
  success: boolean;
  data?: T;
  error?: {
    message: string | string[];
    code?: number;
  };
}

修改路由处理器,统一返回该结构:

// app/api/transactions/route.ts
import { NextRequest, NextResponse } from "next/server";
import { AxiosError } from "axios";
import axios from "../axios";
import { Transaction } from "../../../types";
import { ApiResponse } from "../../../types/api";

interface BackendError {
  message: string[];
  statusCode: number;
}

export async function GET(request: NextRequest) {
  try {
    const transactionsRes = await axios.get<Transaction[]>(`/transactions`);

    return NextResponse.json<ApiResponse<Transaction[]>>({
      success: true,
      data: transactionsRes.data
    });
  } catch (error) {
    const axiosError = error as AxiosError<BackendError>;
    const errorData = axiosError?.response?.data;
    
    return NextResponse.json<ApiResponse>({
      success: false,
      error: {
        message: errorData?.message || "获取交易数据失败",
        code: errorData?.statusCode || 500
      }
    }, { status: errorData?.statusCode || 500 });
  }
}

2. 前端封装统一请求工具

把请求逻辑封装到工具函数中,自动处理响应格式和错误抛出,组件无需关注类型判断:

// utils/fetchApi.ts
import { ApiResponse } from "@/types/api";

export async function fetchApi<T>(url: string): Promise<T> {
  const res = await fetch(url);
  const response: ApiResponse<T> = await res.json();

  if (!response.success) {
    const errorMessage = Array.isArray(response.error?.message) 
      ? response.error.message.join(", ") 
      : response.error?.message || "请求失败";
    throw new Error(errorMessage);
  }

  return response.data as T;
}

组件中直接使用:

// MyComponent.tsx
import { fetchApi } from "@/utils/fetchApi";
import { Transaction } from "../../types";

export const MyComponent = async() => {
  // 直接拿到Transaction[]类型,错误会被error.tsx自动捕获
  const transactions = await fetchApi<Transaction[]>('api/transactions');
  ...
}

3. 基于HTTP状态码的前置判断

如果不想修改后端响应格式,可以利用HTTP状态码提前识别错误:

// utils/fetchApi.ts 优化版
export async function fetchApi<T>(url: string): Promise<T> {
  const res = await fetch(url);
  
  // 非2xx状态码直接抛出错误
  if (!res.ok) {
    const errorRes = await res.json().catch(() => null);
    const message = errorRes?.error?.message || `${res.status} 请求失败`;
    throw new Error(message);
  }

  // 这里返回的就是纯成功数据类型
  const data = await res.json();
  return data as T;
}

4. 强化错误边界的处理能力

  • 保持error.tsx作为页面级错误边界,捕获组件树中的异步错误。
  • 可以封装自定义错误类,携带状态码、错误类型等信息,让error.tsx能展示更精准的提示:
// types/errors.ts
export class ApiError extends Error {
  statusCode: number;

  constructor(message: string, statusCode: number) {
    super(message);
    this.statusCode = statusCode;
    this.name = "ApiError";
  }
}

在请求工具中抛出这个自定义错误,error.tsx就能根据错误类型展示不同内容。


总结

优先推荐统一API响应格式的方案,它能让前后端错误处理逻辑更一致,彻底消除联合类型的混乱。如果无法修改后端,则选择基于HTTP状态码的前置判断方案,同样能简化前端类型处理。两种方案都能让代码更易维护和扩展。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.27 05:12:40