Strapi 空白应用示例精讲:从零启动、构建与部署一个最小可用的 Headless CMS
本文以 Strapi 仓库中 examples/empty 目录下的空白应用模板(empty example)为主体,完整讲解官方 Getting started with Strapi 入门指南所覆盖的 develop、start、build、deploy 四类命令的实际用法与底层实现,并逐一解读该模板的配置文件、入口文件与 TypeScript 编译规则,帮助读者在不依赖任何脚手架预设的情况下,掌握从零启动、定制并部署一个最小可用 Strapi 项目的完整能力。
一、empty 模板是什么:最小化应用骨架
examples/empty 是 Strapi 官方维护的一个"空白起步"应用示例,它的 package.json 中自述为 "description": "A Strapi application",private: true 表明它面向本地开发而非发布。与 examples/complex、examples/getstarted 等携带大量内容类型的示例不同,empty 模板刻意将 src/api/ 目录留空,只保留让一个 Strapi 项目跑起来所必需的最小结构:
examples/empty/
├── config/ # 应用配置(admin、api、database、features、middlewares、plugins、server)
├── database/ # 数据库迁移目录
├── public/uploads/ # 本地上传文件目录
├── src/
│ ├── admin/ # 管理面板前端入口(app.tsx)
│ ├── api/ # 内容类型目录(模板中为空)
│ ├── extensions/ # 应用级扩展目录(模板中为空)
│ └── index.ts # 服务端生命周期入口
├── tsconfig.json # TypeScript 编译配置
└── package.json # 依赖与 npm scripts
从 examples/empty/package.json 可以看到该模板的运行前提:
- Node 版本要求:
"node": ">=20.0.0 <=26.x.x","npm": ">=6.0.0"(engines字段); - 核心依赖:
@strapi/strapi与@strapi/plugin-users-permissions(本仓库内均以workspace:*引用源码包)、better-sqlite3(SQLite 驱动)、以及管理面板所需的react、react-dom、react-router-dom、styled-components; - 开发依赖:
typescript及对应类型定义; - 遥测标识:
"strapi": { "uuid": "getstarted" }字段用于标记该应用实例。
理解了这一骨架之后,README 中给出的四条命令就有了明确的落点。
二、快速启动:README 核心命令逐一拆解
examples/empty/README.md 将入门流程归纳为 develop、start、build 与部署四部分,全部通过 npm scripts 暴露。
1. develop:开发模式(自动重载)
npm run develop
# or
yarn develop
该命令对应 strapi develop。从源码 packages/core/strapi/src/cli/commands/develop.ts 可以看到它实际支持的完整参数集:
| 参数 | 默认值 | 说明 |
|---|---|---|
--bundler [bundler] |
vite |
管理面板打包器,可选 webpack 或 vite(使用 webpack 会收到弃用警告,提示迁移到 vite) |
-d, --debug |
false |
启用调试模式与详细日志 |
--silent |
false |
不输出任何日志 |
--polling |
false |
在网络目录中通过轮询监听文件变更 |
--watch-admin / --no-watch-admin |
开启 | 是否监听管理面板热更新 |
--build-admin / --no-build-admin |
开启 | 监听关闭时是否仍构建管理面板 |
--open |
true |
启动后自动在浏览器打开管理面板 |
--install-deps |
true |
自动安装缺失的管理面板依赖 |
develop 还有一个别名 dev(package.json 中的 npm run dev 与 npm run develop 等价),并且命令内部会判断 cluster.isPrimary,主进程才会执行打包器检查与启动逻辑——这说明 strapi develop 采用了 Node.js 的 cluster 模型来组织进程。
2. start:生产模式(关闭自动重载)
npm run start
# or
yarn start
该命令对应 strapi start,启动时自动重载(autoReload)被禁用,适合部署到服务器长期运行。其 CLI 定义位于 packages/core/strapi/src/cli/commands/start.ts,与 develop 命令同属 packages/core/strapi/src/cli/commands/ 命令族。
3. build:仅构建管理面板
npm run build
# or
yarn build
对应 strapi build,只执行管理面板前端的构建产物生成,不启动服务,常用于 CI/CD 流水线中的构建阶段。
4. 部署:strapi deploy
README 的 Deployment 部分给出了部署入口命令:
yarn strapi deploy
在 empty 模板的 package.json 中,deploy、strapi、console 三个脚本也一并被暴露:
"scripts": {
"build": "strapi build",
"console": "strapi console",
"deploy": "strapi deploy",
"dev": "strapi develop",
"develop": "strapi develop",
"start": "strapi start",
"strapi": "strapi",
"upgrade": "npx @strapi/upgrade latest",
"upgrade:dry": "npx @strapi/upgrade latest --dry"
}
其中 npm run strapi 允许直接透传任意 strapi 子命令;upgrade / upgrade:dry 两个脚本则提供了通过 @strapi/upgrade 升级应用(--dry 表示只做预演不实际执行)的能力。
三、配置层深读:config/ 目录全解
empty 模板在 examples/empty/config/ 下提供了七个 TypeScript 配置文件,每一个都展示了 Strapi 配置函数的标准形态——接收 { env } 参数并返回类型化配置对象。
1. server.ts:监听地址与应用密钥
examples/empty/config/server.ts 定义了服务监听行为:
const serverConfig = ({ env }: Core.Config.Shared.ConfigParams): Core.Config.Server => ({
host: env('HOST', '0.0.0.0'),
port: env.int('PORT', 1337),
app: {
keys: env.array('APP_KEYS', ['toBeModified1', 'toBeModified2']),
},
});
HOST默认0.0.0.0,即监听所有网络接口,容器化部署友好;PORT默认1337,即 Strapi 的经典端口;APP_KEYS用于会话等加密操作,默认值toBeModified*明确提示生产环境必须替换。
2. admin.ts:后台密钥与功能开关
examples/empty/config/admin.ts 集中管理后台安全相关的四类密钥与三个功能标志:
| 环境变量 | 用途 |
|---|---|
ADMIN_JWT_SECRET |
后台登录态 JWT 签名密钥 |
API_TOKEN_SALT |
API Token 加盐 |
ENCRYPTION_KEY |
敏感配置项的对称加密密钥 |
TRANSFER_TOKEN_SALT |
数据导入导出(transfer)token 加盐 |
FLAG_NPS / FLAG_PROMOTE_EE / FLAG_DOC_LINKS |
控制 NPS 调研、EE 推广入口、文档链接的展示开关 |
所有密钥都给了 example-* 占位默认值,同样意在提醒:真实项目必须通过环境变量注入。
3. api.ts:REST 查询默认行为
examples/empty/config/api.ts 只有十余行,却决定了内容 API 的分页语义:
const config: Core.Config.Api = {
rest: {
defaultLimit: 25, // 未传 limit 时每页返回 25 条
maxLimit: 100, // limit 上限 100
withCount: true, // 响应 meta 中附带 total
},
};
4. middlewares.ts:中间件执行顺序
examples/empty/config/middlewares.ts 按固定顺序声明了九个核心中间件:strapi::logger、strapi::errors、strapi::security、strapi::cors、strapi::poweredBy、strapi::query、strapi::body、strapi::session、strapi::favicon、strapi::public。这个顺序本身有讲究:日志与错误处理最先,安全与 CORS 前置,请求体解析(body)在查询(query)之后、会话(session)之前,静态资源(public)兜底在末尾。
5. database.ts:三数据库客户端的完整实现
examples/empty/config/database.ts 是模板中信息密度最高的配置文件,完整覆盖了 SQLite、MySQL、PostgreSQL 三种客户端:
- 客户端选择:
env('DATABASE_CLIENT', 'sqlite'),默认使用零外部依赖的 SQLite;并通过isDatabaseClientKind(来自 packages/core/database 的@strapi/database)做合法性校验,非法值直接抛出Unsupported DATABASE_CLIENT错误,把配置错误暴露在启动阶段而非运行期。 - SQLite 分支:
filename指向env('DATABASE_FILENAME', '.tmp/data.db'),即项目内.tmp目录下的本地文件,并设置useNullAsDefault: true。 - MySQL 分支:默认端口
3306,库名/用户/密码默认均为strapi,支持完整 SSL 参数组(DATABASE_SSL_KEY、DATABASE_SSL_CERT、DATABASE_SSL_CA、DATABASE_SSL_CAPATH、DATABASE_SSL_CIPHER、DATABASE_SSL_REJECT_UNAUTHORIZED)。 - PostgreSQL 分支:默认端口
5432,额外支持DATABASE_SCHEMA(默认public)与DATABASE_URL连接串两种配置方式。 - 连接池与超时:三个客户端统一配置
pool: { min: DATABASE_POOL_MIN(2), max: DATABASE_POOL_MAX(10) },并在connection上附加acquireConnectionTimeout(DATABASE_CONNECTION_TIMEOUT,默认 60000ms)。
这套"env 驱动 + 类型化返回 + 启动期校验"的写法,是 Strapi v5 配置体系的典型范式。
6. plugins.ts 与 features.ts:留白即默认
examples/empty/config/plugins.ts 与 examples/empty/config/features.ts 都返回空对象 {}——这正是"empty"二字的含义:不覆盖任何插件配置、不启用任何额外功能,全部走 Strapi 内核默认值。
四、入口文件:生命周期钩子与面板引导
1. src/index.ts:服务端 register / bootstrap
examples/empty/src/index.ts 导出了标准的双钩子结构:
register({ strapi }):应用初始化之前运行,适合扩展代码(如注册自定义服务);bootstrap({ strapi }):应用启动前运行,适合初始化数据模型、执行定时任务或特殊逻辑。
模板中两者都是空实现,但类型注释 Core.Strapi 明确了钩子参数签名。
2. src/admin/app.tsx:管理面板前端引导
examples/empty/src/admin/app.tsx 是管理面板的前端入口,声明了 locales: ['fr'](声明需要打包的法语文案)与一个会打印 app 对象的 bootstrap 回调。该文件由前端构建链路处理,而非服务端编译。
五、TypeScript 编译边界:tsconfig.json 的两个关键排除
examples/empty/tsconfig.json 采用 strict: true、module: CommonJS、target: ES2019 的编译配置,并通过 exclude 划定了服务端编译的边界:
- 排除
src/admin/:管理面板文件不参与服务端编译(前端走独立的 vite 构建,develop命令默认使用 vite 打包器); - 排除
src/plugins/**、**/*.test.*、.tmp/、.strapi/等:避免运行时生成物与测试代码混入产物。
这意味着修改 src/index.ts 与 src/admin/app.tsx 后,两条构建链路的生效方式不同:前者随服务重启生效,后者依赖 develop 的热监听(--watch-admin 默认开启)。
六、小结
examples/empty 的价值不在于功能,而在于它用最少的代码呈现了 Strapi 应用的标准契约:develop/start/build/deploy 四类命令(其参数细节可从 develop 命令实现 直接查证)、七个配置文件各自负责的领域(端口、密钥、分页、中间件顺序、数据库)、以及 register/bootstrap 双钩子加前后端分离的双入口结构。拿到这个模板后,往 src/api/ 中添加内容类型、按需打开 config/plugins.ts,即可平滑演进为一个生产级 Headless CMS 项目。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0624
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00