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

NextJS 14 App Router中RSC与RCC的类型安全第三方API获取方案

Next.js 14中RSC与RCC类型安全调用第三方API并处理数据更新的方案

一、实现类型安全的数据获取

1. 定义API返回数据的类型

先明确第三方API返回的用户结构,提前定义对应TypeScript类型:

// 单个用户的类型(根据实际API返回字段调整)
export type User = {
  id: string;
  name: string;
  email: string;
  // 补充其他字段,如createdAt、role等
};

// API返回的用户列表类型
export type GetUsersResponse = User[];

2. 封装类型安全的服务器端API调用

把fetch逻辑封装成单独的服务器端函数,集中处理授权、类型校验和错误:

// app/api/users/server-actions.ts
import { getSession } from "@/lib/auth"; // 替换为你的会话获取逻辑
import { z } from "zod"; // 可选:用zod做严格的schema校验

// 可选:用zod定义schema,确保返回数据符合预期
const UserSchema = z.object({
  id: z.string(),
  name: z.string(),
  email: z.string(),
});
const GetUsersResponseSchema = z.array(UserSchema);

export async function fetchUsers(): Promise<GetUsersResponse> {
  const session = await getSession();
  if (!session?.user?.access_token) {
    throw new Error("未授权,无法获取用户数据");
  }

  const res = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/users/all`, {
    headers: {
      "Content-Type": "application/json",
      Authorization: `Bearer ${session.user.access_token}`,
    },
    next: { tags: ["users"] }, // 标记缓存标签,用于后续重新验证
  });

  if (!res.ok) {
    const errorMsg = await res.json().catch(() => res.statusText);
    throw new Error(`获取用户失败:${errorMsg}`);
  }

  const rawData = await res.json();
  // 用zod校验并转换类型,不符合则抛出错误
  return GetUsersResponseSchema.parse(rawData);
  // 若不用zod,也可以用类型断言:return rawData as GetUsersResponse;
}

3. 在RSC页面中调用封装函数

修改Page.tsx,此时users会自动获得正确的类型,不再是any:

// app/users/Page.tsx
import { fetchUsers } from "@/app/api/users/server-actions";
import Table from "./Table";

type SearchParamsType = {
  searchParams: {
    [key: string]: string | string[] | undefined;
  };
};

export async function Page({ searchParams }: SearchParamsType) {
  try {
    const users = await fetchUsers();
    return <Table users={users} />;
  } catch (error) {
    return <div>加载用户失败:{(error as Error).message}</div>;
  }
}

二、处理RCC修改数据后的重新获取

由于Table是客户端组件,无法直接触发服务器端的缓存更新,我们可以通过Server Action + 缓存标签重验证实现:

1. 创建触发重验证的Server Action

// app/api/users/server-actions.ts
"use server"; // 标记为Server Action,只能在服务器端执行

import { revalidateTag } from "next/cache";

export async function revalidateUsers() {
  // 重验证标记为"users"的缓存数据,触发RSC重新获取用户列表
  await revalidateTag("users");
}

2. 在客户端组件Table中调用该Action

当用户完成数据修改(编辑/删除等)后,调用revalidateUsers触发数据更新:

// app/users/Table.tsx
"use client"; // 标记为客户端组件

import { User } from "@/app/api/users/server-actions";
import { revalidateUsers } from "@/app/api/users/server-actions";

type TableProps = {
  users: User[];
};

export default function Table({ users }: TableProps) {
  const handleUserEdit = async (userId: string, newName: string) => {
    // 执行客户端的修改请求
    const updateRes = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/users/${userId}`, {
      method: "PUT",
      headers: {
        "Content-Type": "application/json",
        // 客户端获取token需使用安全方式,比如从auth库的客户端会话或localStorage(注意XSS风险)
        Authorization: `Bearer ${localStorage.getItem("access_token")}`,
      },
      body: JSON.stringify({ name: newName }),
    });

    if (updateRes.ok) {
      // 修改成功后触发缓存重验证,让RSC重新拉取最新数据
      await revalidateUsers();
    }
  };

  return (
    <table>
      <thead>
        <tr>
          <th>姓名</th>
          <th>邮箱</th>
          <th>操作</th>
        </tr>
      </thead>
      <tbody>
        {users.map(user => (
          <tr key={user.id}>
            <td>{user.name}</td>
            <td>{user.email}</td>
            <td>
              <button onClick={() => handleUserEdit(user.id, "新名字")}>编辑</button>
            </td>
          </tr>
        ))}
      </tbody>
    </table>
  );
}

额外注意事项

  • 客户端授权:避免在客户端直接硬编码或暴露敏感token,建议使用NextAuth等库的客户端会话方法获取授权信息。
  • 错误处理:可以在Server Action和客户端请求中添加更细致的错误捕获,比如网络异常、权限不足等场景的提示。

内容的提问来源于stack exchange,提问作者Bert Van Hecke

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.26 06:05:11