使用 Next.js 构建大规模 SaaS 的集中式 Action
发布: (2025年12月4日 GMT+8 16:49)
5 min read
原文: Dev.to
Source: Dev.to
架构概览
在大型 SaaS 项目中,若在每个模块中单独处理服务器动作会导致代码重复。
集中式变更引擎 将所有 API 变更统一到一个可复用的函数中。它负责身份验证、授权、店铺/子域名上下文、请求负载、fetch 执行以及缓存重新验证。
核心工具
- logger – 用于调试和监控的结构化日志。
- resolveModelName – 将路由名称如
saved-attribute转换为saved_attribute。 - hasPermission 与 parseAccessRules – 实现 RBAC,检查用户是否可以执行特定操作。
- getBaseUrlWithSubdomain – 根据店铺子域名解析正确的 API 基础 URL。
身份验证处理
if (requireAuth) {
const accessToken = cookieStore.get("accessToken")?.value;
if (!accessToken) {
return { message: "You are not authorized to perform this action!" };
}
// Validate JWT access token...
}
店铺 / 子域名上下文
if (requireShopId) {
const subdomain = cookieStore.get("shopSubdomain")?.value;
if (!subdomain) {
return { message: "Shop identifier not found!" };
}
// Use subdomain for multi‑tenant routing...
}
基于角色的访问控制 (RBAC)
const rulesRaw = cookieStore.get("accessRules")?.value;
const rules = parseAccessRules(rulesRaw);
const model = resolveModelName(route.split("/")[1]);
const action = methodToAction[method];
if (!hasPermission(rules, model, action)) {
return { success: false, message: "Permission denied" };
}
将 HTTP 方法映射为 CRUD 操作。
动态负载处理
let body: string | undefined = undefined;
if (method === "DELETE" && ids?.length) {
body = JSON.stringify({ ids });
} else if (method !== "DELETE" && data) {
body = data;
}
支持单 ID 删除、批量删除以及 POST/PUT 负载。
Fetch 执行
const response = await fetch(url, {
method,
headers,
body,
cache: "no-store",
});
const result = await response.json();
使用 no-store 选项执行服务器端变更,确保获取最新数据。
缓存重新验证
paths.forEach(path => typeof path === "string" && revalidatePath(path));
tags.forEach(tag => typeof tag === "string" && revalidateTag(tag));
在变更后自动重新验证 Next.js 路径或标签。
错误处理与日志记录
try {
// ...mutation logic...
} catch (error) {
logger(error);
return { success: false, message: "An error occurred during the mutation." };
}
统一错误处理。
好处
- 可复用性 – 所有模型共用单一变更处理器。
- 安全性 – 全局 RBAC 强制执行。
- 多租户就绪 – 处理店铺特定子域名。
- 一致性 – 统一日志、错误处理和缓存重新验证。
- 可扩展性 – 随 SaaS 规模增长易于扩展。
使用示例
await mutation({
route: "/customer",
method: "POST",
data: JSON.stringify({ name: "John Doe" }),
pathToRevalidate: "/customers",
tagsToRevalidate: ["customer‑list"],
});
一次调用即可处理身份验证、RBAC、负载以及缓存失效。
流程图
┌────────────────────┐
│ Client / Frontend │
│ (React / Next.js) │
└─────────┬──────────┘
│
▼
┌────────────────────┐
│ Server Action Call│
│ mutation(params) │
└─────────┬──────────┘
│
┌────────┴─────────┐
│ │
▼ ▼
┌───────────────┐ ┌───────────────┐
│ AUTHENTICATION│ │ SHOP / SUBDOMAIN│
│ check JWT token│ │ get shop identifier│
│ from cookies │ │ from cookies │
└───────┬───────┘ └───────┬───────┘
│ │
└───────┬─────────┘
▼
┌─────────────────────┐
│ ROLE‑BASED ACCESS │
│ CONTROL (RBAC) │
│ hasPermission() │
│ parseAccessRules() │
└───────┬─────────────┘
▼
┌─────────────────────┐
│ Construct Headers │
│ - Authorization │
│ - x‑app‑identifier │
└───────┬─────────────┘
▼
┌─────────────────────┐
│ Determine Payload │
│ - DELETE ids array │
│ - POST / PUT data │
└───────┬─────────────┘
▼
┌─────────────────────┐
│ FETCH Request │
│ method, headers, │
│ body │
└───────┬─────────────┘
▼
┌─────────────────────┐
│ Response Handling │
│ - parse JSON │
│ - log errors │
└───────┬─────────────┘
▼
┌───────────────┬───────────────┐
▼ ▼ ▼
┌───────┐ ┌───────┐ ┌───────┐
│ PATH │ │ TAG │ │ CLIENT│
│ REVAL │ │ REVAL │ │ UI │
└───────┘ └───────┘ └───────┘
要点
- 集中式服务器动作减少样板代码并提升可维护性。
- RBAC 确保所有 API 变更拥有一致的安全性。
- 支持子域名感知的多租户架构,满足 SaaS 可扩展性需求。
- 自动缓存重新验证保证 UI 始终展示最新数据。
在大型 SaaS 项目中实现集中式变更引擎,可显著提升代码可维护性、安全性和可扩展性。通过将身份验证、RBAC、多租户子域名处理、负载管理以及缓存重新验证统一到单一服务器动作,开发者能够:
- 消除多个端点之间的重复代码。
- 通过动态权限检查强制统一的安全策略。
- 通过子域名感知的 API 调用高效支持多租户架构。
- 通过自动重新验证 Next.js 路径和标签确保 UI 一致性。
- 随着 SaaS 的增长轻松扩展,添加新模型和操作时几乎没有摩擦。