Strapi example-js 模板实战:从 develop 启动、build 构建到 strapi deploy 部署的完整工程链路
本篇基于 Strapi 仓库中 create-strapi-app CLI 的 JS 示例模板(packages/cli/create-strapi-app/templates/example-js)及其自带的 README.md 展开。该文档是脚手架生成的 Strapi 应用根目录下的"上手指南",核心讲 develop、start、build 三个常用命令与 strapi deploy 部署流程。读完本文,你将不仅知道如何启动一个 Strapi 示例项目,还能深入理解模板的脚本映射、环境变量驱动的数据库/服务配置,以及种子脚本 seed.js 的首次运行检测与内容导入机制。
模板定位:create-strapi-app 的 JS 示例工程
create-strapi-app 是 Strapi 官方脚手架包,package.json 中声明其职责为 "Generate a new Strapi application.",当前仓库内版本为 5.52.2,要求 Node.js >=20.0.0 <=26.x.x。该包通过 files 字段把 templates/ 目录一并发布,仓库中提供了四套模板:
packages/cli/create-strapi-app/templates/vanilla/vanilla-js:空白项目;packages/cli/create-strapi-app/templates/example/example-js:预置文章、作者、分类等内容模型与种子数据的示例项目。
example-js 是面向 JavaScript 开发者的示例工程,其根目录结构(以仓库实际内容为准)为:
example-js/
├── config/ # admin.js、api.js、database.js、middlewares.js、plugins.js、server.js
├── data/ # data.json 种子数据 + uploads/ 示例媒体文件
├── database/ # migrations/ 数据库迁移目录
├── public/uploads/ # 运行时上传目录
├── scripts/seed.js # 种子脚本
├── src/ # admin/、api/、components/、extensions/、index.js
├── jsconfig.json
├── package.json
└── README.md # 即本篇的关联文档
其中 src/api/ 下内置了 about、article、author、category、global 五个内容类型(各自包含 content-types、controllers、routes、services 四层文件),src/components/shared/ 下则提供了 media、quote、rich-text、seo、slider 五个可复用组件。这套模型让示例项目开箱即是一个可浏览的博客内容站,也决定了种子脚本需要导入哪些数据。
develop:开发模式启动(autoReload 开启)
模板 README 的第一个命令是 develop,用于以开启 autoReload 的方式启动应用,适合本地开发时热重载配置与代码变更:
npm run develop
# or
yarn develop
对照 package.json,这条 npm script 的真实映射是:
{
"scripts": {
"build": "strapi build",
"deploy": "strapi deploy",
"develop": "strapi develop",
"dev": "strapi develop",
"console": "strapi console",
"start": "strapi start",
"strapi": "strapi",
"upgrade": "npx @strapi/upgrade latest",
"upgrade:dry": "npx @strapi/upgrade latest --dry",
"seed:example": "node ./scripts/seed.js"
}
}
即 npm run develop 最终执行的是 Strapi 内置 CLI 的 strapi develop 子命令。注意这里同时保留了 dev 别名,两条命令等价。
start:生产模式启动(autoReload 关闭)
第二个命令是 start,以关闭 autoReload 的方式启动应用,对应生产环境运行场景:
npm run start
# or
yarn start
映射关系同样是 strapi start。develop 与 start 的区别仅在是否开启自动重载:前者监听文件变化并自动重启,便于开发迭代;后者以固定加载态运行,行为可预期,适合作为部署后的常驻进程。从模板脚本结构看,二者共用同一份 config/ 与 src/,差异只体现在 CLI 层的运行策略上。
build:构建管理后台(admin panel)
第三个命令用于构建后台管理面板的静态资源:
npm run build
# or
yarn build
映射为 strapi build。Strapi 的 admin 端是基于前端工程打包的 SPA,构建产物随应用一起分发;example-js 的 src/admin/ 目录中带有 app.example.js 与 vite.config.example.js 两个示例文件,作为管理员面板定制(插件注册、Vite 配置扩展)的起点,默认处于 .example 示例态、不参与运行。
模板中的其他实用脚本
除 README 重点介绍的三个命令外,package.json 还预置了若干工程化脚本,实际使用示例项目时值得关注:
| 脚本 | 实际命令 | 用途 |
|---|---|---|
npm run console |
strapi console |
打开 Strapi 交互式控制台,直接调用 strapi 运行时 API |
npm run seed:example |
node ./scripts/seed.js |
导入模板种子数据(内容 + 媒体) |
npm run upgrade |
npx @strapi/upgrade latest |
执行 Strapi 升级流程 |
npm run upgrade:dry |
npx @strapi/upgrade latest --dry |
以 dry-run 模式预演升级,不落盘 |
npm run deploy |
strapi deploy |
执行部署(见下文) |
npm run strapi ... |
strapi ... |
透传任意 Strapi CLI 子命令 |
配置层:环境变量驱动的服务与数据库
develop/start 启动时加载的核心配置都在 config/ 目录。
服务配置 server.js
server.js 决定 API 服务监听行为:
module.exports = ({ env }) => ({
host: env('HOST', '0.0.0.0'),
port: env.int('PORT', 1337),
app: {
keys: env.array('APP_KEYS'),
},
webhooks: {
populateRelations: env.bool('WEBHOOKS_POPULATE_RELATIONS', false),
},
});
- 默认监听
0.0.0.0:1337,Strapi 的 1337 端口由此而来,可用HOST/PORT环境变量覆盖; APP_KEYS是逗号分隔的签名密钥数组,用于 API Token 等签名场景;WEBHOOKS_POPULATE_RELATIONS控制 Webhook 载荷是否展开关系字段,默认false。
管理端配置 admin.js
admin.js 集中了后台安全相关的密钥,全部来自环境变量:
ADMIN_JWT_SECRET:管理员会话 JWT 签名密钥;API_TOKEN_SALT:API Token 派生盐值;TRANSFER_TOKEN_SALT:数据迁移/传输令牌盐值;ENCRYPTION_KEY:管理端敏感信息加密密钥;flags下的nps、promoteEE、docLinks三个布尔开关控制后台内的调研问卷、EE 推广与文档链接展示,均可通过FLAG_NPS、FLAG_PROMOTE_EE、FLAG_DOC_LINKS关闭。
数据库配置 database.js
database.js 展示了模板对多数据库的完整支持,支持 sqlite(默认)、mysql、postgres 三种 DATABASE_CLIENT,并带有校验逻辑:
const client = env('DATABASE_CLIENT', 'sqlite');
if (!isDatabaseClientKind(client)) {
throw new Error(
`Unsupported DATABASE_CLIENT: ${client}. Use "postgres", "mysql", or "sqlite".`
);
}
三种连接的要点:
- sqlite(默认):文件型数据库,默认落在项目相对路径
.tmp/data.db(path.join(__dirname, '..', env('DATABASE_FILENAME', '.tmp/data.db'))),useNullAsDefault: true; - mysql:默认
localhost:3306,库名strapi,用户/密码strapi,连接池min=2, max=10(DATABASE_POOL_MIN/DATABASE_POOL_MAX),支持DATABASE_SSL及其 key/cert/ca 等证书项; - postgres:默认
localhost:5432,支持DATABASE_URL连接串与DATABASE_SCHEMA(默认public),SSL 配置项与 mysql 一致。
所有连接还会应用 DATABASE_CONNECTION_TIMEOUT(默认 60000 ms)作为获取连接的超时时间。这意味着本地零配置即可运行(sqlite 落盘 .tmp/),而在托管数据库环境下只需要注入 DATABASE_CLIENT 与相应连接变量。
种子数据脚本 seed.js 深度解析
npm run seed:example 执行的 scripts/seed.js 是模板最有技术含量的部分,完整演示了"如何在 Strapi 运行时之外以编程方式创建内容"。其关键机制如下:
1. 首次运行检测(plugin store 标记位)
async function isFirstRun() {
const pluginStore = strapi.store({
environment: strapi.config.environment,
type: 'type',
name: 'setup',
});
const initHasRun = await pluginStore.get({ key: 'initHasRun' });
await pluginStore.set({ key: 'initHasRun', value: true });
return !initHasRun;
}
通过 Strapi 的 plugin store 写入 initHasRun 标记,第二次运行时直接提示 "Seed data has already been imported"。从源码结构看,这是一个幂等性保护:重复导入会因唯一约束或重复内容失败,因此除非清空数据库,否则不做二次导入。
2. 打开 public 角色读取权限
setPublicPermissions 会查找 plugin::users-permissions.role 中 type: 'public' 的角色,然后为 article、category、author、global、about 五个控制器批量创建 find/findOne 权限:
action: `api::${controller}.${controller}.${action}`,
这解释了为什么示例项目在启动后无需任何后台操作,前端就能匿名 GET /api/articles 等接口——种子脚本替 public 角色完成了授权。
3. 媒体上传去重与 blocks 回填
uploadFile调用strapi.plugin('upload').service('upload'),即 upload 插件的上传服务,逐文件生成fileInfo(替代文本、标题、名称);checkFileExistsBeforeUpload先按文件名(去掉扩展名)查询plugin::upload.file,已存在则复用,不存在才从data/uploads/读文件上传,并用mime-types推断 MIME;updateBlocks遍历动态区(dynamic zone)的 blocks:对shared.media块回填单文件、对shared.slider块回填文件数组,其余块原样保留。
这套"先查库、后上传、按 __component 回填"的写法,正是 Strapi 动态区 + 组件模型(对应 src/components/shared/ 下的 media.json、slider.json 等 schema)在种子脚本中的标准消费姿势。
4. 导入顺序与发布态
importSeedData 的执行顺序为:授权 → categories → authors(先传头像再建作者)→ articles(封面 + blocks 处理)→ global(favicon 与默认分享图)→ about。每个入口条目都显式设置 publishedAt: Date.now(),确保导入的是已发布而非草稿态,API 立即可见。
5. 独立进程启动运行时
脚本末尾 main 函数展示了脱离 Web 服务、仅加载 Strapi 运行时的标准方式:
const { createStrapi, compileStrapi } = require('@strapi/strapi');
const appContext = await compileStrapi();
const app = await createStrapi(appContext).load();
// ... 执行 seed ...
await app.destroy();
即 compileStrapi() 编译上下文 → createStrapi().load() 加载应用 → 执行种子逻辑 → destroy() 释放。这一模式同样适用于编写一次性数据迁移脚本。
部署:strapi deploy 与部署选项
模板 README 的部署章节指出 Strapi 支持多种部署方案,并给出核心命令:
yarn strapi deploy
对照 package.json,该命令对应 "deploy": "strapi deploy" 脚本(yarn strapi deploy 则是通过 "strapi": "strapi" 脚本透传子命令,两者等价)。部署前通常需要先执行 npm run build 产出管理后台静态资源;在数据库层面则按上文 DATABASE_CLIENT 环境变量接入目标数据库,并注入 ADMIN_JWT_SECRET、API_TOKEN_SALT、APP_KEYS 等安全变量。仓库的 examples/ 目录与 docker-compose.dev.yml 等文件也提供了开发/测试环境组合的参考。
小结
example-js 模板 README 虽短,但它给出的 develop/start/build/deploy 四条命令覆盖了 Strapi 应用从开发到上线的完整生命周期;而围绕这份 README,模板工程内的 package.json 脚本映射、config/ 下环境变量驱动的服务与数据库配置、以及 scripts/seed.js 的运行时编程式导入,共同构成了一套可直接复制的 JS 版 Strapi 工程基线。以它为起点,替换 src/api/ 中的内容类型、调整 config/database.js 的目标数据库,即可得到属于自己的 Strapi 应用骨架。
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 StartedRust0625
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