news 2026/9/7 18:56:10

Next.js 与 Payload 同仓 Serverless 部署:@payloadcms/next-payload 集成实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Next.js 与 Payload 同仓 Serverless 部署:@payloadcms/next-payload 集成实战

Next.js 与 Payload 同仓 Serverless 部署:@payloadcms/next-payload 集成实战

【免费下载链接】next.jsThe React Framework项目地址: https://gitcode.com/GitHub_Trending/next/next.js

本篇指南围绕 Next.js 仓库中的 cms-payload 示例 展开,讲解如何使用@payloadcms/next-payload将 Payload CMS 以 Serverless 方式与 Next.js 应用部署在同一个仓库中:涵盖本地开发启动流程、所需环境变量、withPayload配置改造、Admin 界面挂载位置,以及内容变更触发页面按需再水合的完整链路。读完之后,你可以独立搭建一套「Next.js 前端 + Payload 内容管理」的同仓部署方案,并理解其背后的实现机制。

方案定位:为什么把 Payload 放进 Next.js

Payload 本身是一个基于 Node.js 的开源 Headless CMS,而 cms-payload 示例 演示的正是官方@payloadcms/next-payload包的能力:让 Payload 以 API 路由和静态资源的形式运行在 Next.js(Pages Router API + App Router Admin)中,从而可以用 Vercel 等边缘/Serverless 平台整体部署,无需单独维护一个 Express 服务。

从 package.json 可以看到该示例的关键依赖组合:

  • payload@1.9.2:Payload 核心;
  • @payloadcms/next-payload@0.0.27:Next.js 集成层,提供withPayload配置函数和 API 处理器;
  • @payloadcms/plugin-cloud-storage+@aws-sdk/client-s3:将媒体文件存入 S3 而非本地磁盘(Serverless 环境的关键前提);
  • @vercel/edge:为运行在 Vercel Edge 环境提供必要的 Node API 垫片。

快速开始:克隆、安装与本地开发

使用 create-next-app 引导示例

按照 README 的说明,用create-next-app即可拉取本示例:

npx create-next-app --example cms-payload cms-payload-app
yarn create next-app --example cms-payload cms-payload-app
pnpm create next-app --example cms-payload cms-payload-app

本地开发的软件依赖

README 列出的本地开发前置条件:

  1. MongoDB(Payload 的数据库);
  2. Node + NPM / Yarn;
  3. 一个用于存储媒体文件的 S3 Bucket(可选)。

启动步骤

  1. 克隆仓库(或使用create-next-app生成的项目);
  2. 运行yarnnpm install
  3. 运行cp .env.example .env,并按.env.example填写全部环境变量;
  4. 运行yarn dev(即next dev)启动开发服务器;
  5. 访问http://localhost:3000/admin即可进入 Payload 管理后台。

部署到云端(如 Vercel)时,核心要求只有两点:一个 Mongo Atlas 数据库连接串,以及一个 S3 Bucket(可选)。把.env.example中的变量替换为真实值即可上线。

环境变量全解

.env.example 是理解整个集成的心跳,逐条说明如下:

变量用途
MONGODB_URIPayload 使用的 MongoDB 连接串,如mongodb://localhost/payload-vercel-functions;Serverless 部署时填 Mongo Atlas 连接串
PAYLOAD_SECRETPayload 的会话签名密钥
PAYLOAD_CONFIG_PATHPayload 配置文件路径,示例中为dist/payload.config.js
NEXT_PUBLIC_APP_URLNext.js 站点的公开地址,默认http://localhost:3000
PAYLOAD_PUBLIC_CMS_URLCMS 的公开地址,用于内容变更后回调解密再生成接口
S3_ACCESS_KEY_ID/S3_SECRET_ACCESS_KEYS3 写权限凭证,供媒体上传使用
S3_REGIONS3 区域
NEXT_PUBLIC_S3_HOSTNAME/NEXT_PUBLIC_S3_BUCKETS3 主机名与 Bucket 名,同时用于next/image的远程图片域名白名单
PAYLOAD_PRIVATE_REGENERATION_SECRETPayload 侧调用再生成接口时携带的密钥
NEXT_PRIVATE_REGENERATION_SECRETNext.js 侧再生成接口校验的密钥

注意最后两个变量的分工:前者是 Payload 发出的请求凭证,后者是 Next.js 接口端用来验签的,二者必须一致才能触发按需再生成(下文详述)。

withPayload:Next.js 配置的改造点

next.config.js展示了集成层的两个关键动作:

const { withPayload } = require("@payloadcms/next-payload"); const nextConfig = withPayload( { reactStrictMode: true, // 将 /admin 下所有路径回退到 Admin 单页应用入口 rewrites: [{ source: "/admin/(.*)", destination: "/admin/index.html" }], images: { remotePatterns: [ { protocol: "https", hostname: "nextjs-vercel.payloadcms.com", port: "", pathname: "/my-account/**", }, { protocol: "https", hostname: process.env.NEXT_PUBLIC_S3_HOSTNAME, port: "", pathname: `/${process.env.NEXT_PUBLIC_S3_BUCKET}/**`, }, ], }, }, { // 指向 Payload 配置文件 configPath: path.resolve(__dirname, "./payload/payload.config.ts"), }, ); module.exports = nextConfig;

三个值得关注的细节:

  1. withPayload包装:它会在构建时为 Payload Admin 生成静态资源,并注入相关 Webpack 规则;第二个参数configPath告诉集成层 payload.config.ts 的位置。
  2. /admin路由重写:Payload Admin 是一个单页应用,Next.js 把/admin/*全部指向/admin/index.html,由前端路由接管深层路径。
  3. remotePatterns白名单:媒体图片来自 S3,必须把NEXT_PUBLIC_S3_HOSTNAME+ Bucket 前缀加入next/image的远程域名白名单,否则<Image>组件会拒绝加载 CMS 中的图片。

Payload 配置:Collection、S3 媒体存储与类型生成

payload/payload.config.ts是本示例的 CMS 配置核心:

import { buildConfig } from "payload/config"; import { cloudStorage } from "@payloadcms/plugin-cloud-storage"; import { s3Adapter } from "@payloadcms/plugin-cloud-storage/s3"; const adapter = s3Adapter({ config: { endpoint: `https://${process.env.NEXT_PUBLIC_S3_HOSTNAME}`, region: process.env.S3_REGION, forcePathStyle: true, credentials: { accessKeyId: process.env.S3_ACCESS_KEY_ID as string, secretAccessKey: process.env.S3_SECRET_ACCESS_KEY as string, }, }, bucket: process.env.NEXT_PUBLIC_S3_BUCKET as string, }); export default buildConfig({ collections: [Pages, Users, Media], globals: [MainMenu], typescript: { outputFile: path.resolve(__dirname, "../payload-types.ts"), }, graphQL: { schemaOutputFile: path.resolve(__dirname, "generated-schema.graphql"), }, plugins: [ cloudStorage({ collections: { media: { adapter, disablePayloadAccessControl: true, }, }, }), ], });

要点解析:

  • S3 适配器是 Serverless 化的关键:Serverless 平台没有持久本地磁盘,媒体文件必须落在对象存储上。这里通过@payloadcms/plugin-cloud-storages3Adapter接管mediacollection 的文件读写,凭证与 Bucket 全部来自环境变量。
  • typescript.outputFile:Payload 会在构建/生成时把每个 Collection 的字段类型输出到 payload-types.ts,前端组件因此获得完整的类型推导。package.json中的generate:types脚本(cross-env PAYLOAD_CONFIG_PATH=payload/payload.config.ts payload generate:types)可手动触发该过程。
  • graphQL.schemaOutputFile:同时导出一份 GraphQL Schema,配合后文的 GraphQL API 路由使用。

API 路由:Pages Router 承载 Payload 请求

Payload 的 REST API 在示例中由 Pages Router 的 API 路由承载。以pages/api/[collection]/index.ts为例:

import handler from "@payloadcms/next-payload/dist/handlers/[collection]"; export default handler; export const config = { api: { bodyParser: false, externalResolver: true, }, };

整个pages/api/[collection]/目录是一套极薄的转发层:index.ts(列表/查询)、[id].ts(单文档读写)、login.tslogout.tsme.tsrefresh.tsfirst-register.tsforgot-password.tsinit.tsaccess/[id].ts等路由各自一行,把请求委托给@payloadcms/next-payload打包好的对应 handler。两个config参数值得注意:

  • bodyParser: false:由 Payload 自行解析请求体(因为它需要处理文件上传等复杂 payload);
  • externalResolver: true:允许这些路由以独立解析器方式挂载,兼容 Serverless 冷启动场景。

此外还有全局与 GraphQL 相关的路由:pages/api/globals/[global]/(全局设置读写)、pages/api/graphql.ts 与pages/api/graphql-playground.ts(GraphQL 端点及调试页)、pages/api/access.ts(权限校验)。

Admin 界面:App Router 中的 Root 组件

Admin 面板位于 App Router 的路由组(payload)中。app/(payload)/admin/page.tsx/admin/page.tsx) 只有十几行:

"use client"; import React from "react"; import Root from "payload/dist/admin/Root"; const PayloadAdmin = () => { const [mounted, setMounted] = React.useState(false); React.useEffect(() => { setMounted(true); }, []); if (!mounted) return null; return <Root />; }; export default PayloadAdmin;

这里用客户端状态确保Root只在浏览器端挂载,避免 SSR 时访问window报错。app/(payload)/admin/[...slug]/page.tsx则把/admin下所有深层路径都渲染为同一组件:

// Need to render the same component for anything within /admin export { default } from "../page";

前端路由的深度路径由 Payload Admin 自己处理,Next.js 侧只需保证「同一个组件兜底一切子路径」。

内容驱动的前端:RSC 读取 + 按需再生成

服务端组件直接查询 Payload

app/(site)/[slug]/page.tsx展示了内容如何流入 Next.js:

import { getPayloadClient } from "../../../payload/payloadClient"; const Page = async ({ params: { slug } }) => { const payload = await getPayloadClient(); const pages = await payload.find({ collection: "pages", where: { slug: { equals: slug || "home" } }, }); const page = pages.docs[0]; if (!page) return notFound(); return ( <> <AdminBar adminBarProps={{ collection: "pages", id: page.id }} /> <Hero {...page.hero} /> <Blocks blocks={page.layout} /> </> ); }; export async function generateStaticParams() { const payload = await getPayloadClient(); const pages = await payload.find({ collection: "pages", limit: 0 }); return pages.docs.map(({ slug }) => ({ slug })); }
  • 页面数据通过getPayloadClient()直连数据库查询(生产部署中通常经由 Payload API),generateStaticParams在构建时枚举全部页面 slug;
  • publishedOnly访问控制(见 payload/access/publishedOnly.ts)保证只有已发布的文档可被前端读取。

Pages collection 定义了一个「标题 + Hero + Blocks 布局 + Slug」的结构,layout字段由CallToActionContentMediaBlock三种块组成,与components/Blocks/下的 React 组件一一对应——这就是典型的「结构化内容 → 服务端组件」映射。

从内容变更到页面再水合的完整链路

这是整个示例最有实战价值的部分,链路由三端拼合:

  1. Payload 侧的 afterChange Hookpayload/utilities/regenerateStaticPage.ts挂在 Pages collection 的afterChange钩子上(见 Pages.ts 中hooks: { afterChange: [regenerateStaticPage] })。文档变更后,它向 Next.js 站点发起请求:

    const res = await fetch( `${process.env.PAYLOAD_PUBLIC_CMS_URL}/api/regenerate?secret=${process.env.PAYLOAD_PRIVATE_REGENERATION_SECRET}&path=${path}`, );
  2. Next.js 侧的再生成接口pages/api/regenerate.ts校验secretNEXT_PRIVATE_REGENERATION_SECRET是否一致,解析出path后调用res.revalidate(path)触发该路径的按需再水合;密钥不匹配返回 401,缺少 path 返回 400。

  3. 效果:编辑者在http://localhost:3000/admin修改并保存页面后,对应静态页面会被立即重新生成,无需整站重建。

这条「Hook → 带密钥的内部 API 调用 → revalidate」的链路,是内容管理与静态生成保持同步的通用模式,也解释了.env.example中两个*_REGENERATION_SECRET变量必须配对的原因。

构建与运行命令

package.json 中的脚本一览:

脚本说明
yarn dev启动next dev开发服务器
yarn build/yarn build:next构建生产版本(withPayload会在构建中一并产出 Admin 资源)
yarn start启动生产服务器
yarn install:payload执行next-payload install,初始化 Payload 集成所需的构建钩子
yarn generate:types生成payload-types.ts类型定义
yarn generate:graphQLSchema导出 GraphQL Schema 文件

小结

这个示例给出的是一套边界清晰的最小可行方案:用withPayload改造 Next.js 配置,Pages Router 转发 Payload REST API,App Router 挂载 Admin 单页应用,S3 承载媒体存储,afterChangeHook +revalidate打通内容同步。所有关键文件集中在 examples/cms-payload 目录下,按「next.config.jspages/api/app/(payload)/payload/」的顺序阅读源码,即可完整还原其实现脉络。若你的项目需要更丰富的第三方 CMS 集成参考,同仓库还有 cms-prismic、cms-contentful、cms-wordpress 等示例可对照阅读。

【免费下载链接】next.jsThe React Framework项目地址: https://gitcode.com/GitHub_Trending/next/next.js

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/7 18:55:41

MySQL建表规范与数据导入导出实战全解析

1. 开始之前&#xff1a;为什么建表和导入导出这么重要最近整理笔记时翻到MySQL建表和导入导出这块&#xff0c;发现看似基础的东西&#xff0c;实际用起来坑真不少。无论是刚入门的新手&#xff0c;还是写了几年SQL的老手&#xff0c;几乎天天要跟这两件事打交道——建表决定数…

作者头像 李华
网站建设 2026/9/7 18:55:24

电影院在线订票系统全流程开发:从数据库设计到答辩实战指南

作为每年计算机毕业设计里被选到烂大街、但仍是最经典的几个题目之一&#xff0c;电影院在线订票系统几乎是“Web开发入门完整业务链路”的教科书式组合。我亲眼见过太多人从选题时的满怀期待&#xff0c;到中期开发时对着座位排布和订单状态一脸茫然&#xff0c;再到最后答辩时…

作者头像 李华
网站建设 2026/9/7 18:55:22

JavaScript前端加解密实战:AES与RSA应用指南

1. JavaScript加解密技术概述在现代Web开发中&#xff0c;数据安全传输与存储已成为基本需求。JavaScript作为前端开发的核心语言&#xff0c;其加解密能力直接关系到用户数据的安全性。不同于传统的服务器端加密&#xff0c;前端加密可以在数据离开客户端前就进行保护&#xf…

作者头像 李华
网站建设 2026/9/7 18:54:23

高校评优管理系统JavaWeb实战:Spring Boot+MyBatis-Plus全解析

做毕设选了“高校评优管理系统”这个题目&#xff0c;用 java 技术栈来落地&#xff0c;本质上是一个非常典型的 JavaWeb 信息管理类项目。这类系统放在多年前&#xff0c;可能还叫 JSP Servlet JDBC 时代的老三样&#xff0c;但放到现在&#xff0c;比较合理的形态是 Spring…

作者头像 李华