首页
/ Strapi TypeScript 示例项目实战:从 CLI 命令到完整项目配置解析(kitchensink-ts)

Strapi TypeScript 示例项目实战:从 CLI 命令到完整项目配置解析(kitchensink-ts)

2026-09-06 12:02:40作者:柯茵沙

本文以 Strapi 仓库内置的 examples/kitchensink-ts 官方 TypeScript 示例工程为主体,围绕该示例 README 中介绍的核心能力展开:如何使用 Strapi CLI 的 developstartbuild 三条命令启动与构建项目,并进一步结合示例工程中的配置文件、应用入口与 TypeScript 配置,给出一个可直接参考的 Strapi TS 项目落地结构。读完本文,你将掌握 Strapi 示例工程的目录组织方式、各配置文件的类型化写法,以及三条 CLI 命令对应的底层脚本映射。

一、示例工程定位与目录结构

examples/kitchensink-ts 是 Strapi 源码仓库中用于演示 TypeScript 环境下完整 Strapi 应用的示例工程。与同目录下的 examples/getstarted(JavaScript 版本)等示例不同,该工程全部后端代码、配置文件和管理端扩展代码均使用 TypeScript 编写,适合作为新项目脚手架的参照模板。

工程的整体目录结构如下(均相对仓库根目录):

路径 作用
config/ 五个核心配置文件:admin.tsapi.tsdatabase.tsmiddlewares.tsserver.ts
src/index.ts 应用入口,导出 register / bootstrap 两个生命周期钩子
src/admin/app.example.tsx 管理面板扩展示例(locales 配置与前端 bootstrap 钩子)
src/admin/webpack.config.example.ts 管理面板 webpack 自定义示例
src/api/ 内容类型(Content-Types)与控制器代码目录,示例中为空
src/extensions/ 自定义扩展(extensions)目录
public/uploads/ 本地文件上传的默认落盘目录
tsconfig.json 后端(CommonJS)TypeScript 编译配置
package.json 脚本命令、依赖与 Node 版本约束

值得注意的是,package.json 的依赖中使用了 workspace:* 协议引用 @strapi/strapi@strapi/plugin-users-permissions,说明该工程位于 Strapi 仓库的 yarn workspaces 体系内,可直接引用源码中的 Strapi 核心包,这正是示例工程能随仓库版本同步演进的实现方式。

二、三条核心 CLI 命令:develop / start / build

示例 README(examples/kitchensink-ts/README.md)开篇即指出:Strapi 自带功能完整的命令行工具(CLI),可以在数秒内完成项目的脚手架搭建与日常管理。工程提供的三条核心命令如下。

2.1 develop:开发模式启动(启用 autoReload)

npm run develop
# or
yarn develop

以自动热重载(autoReload)模式启动 Strapi 应用,适合开发阶段使用:修改代码后应用会自动重新加载,无需手动重启进程。

2.2 start:生产模式启动(禁用 autoReload)

npm run start
# or
yarn start

以关闭 autoReload 的模式启动应用,是部署与生产运行对应的启动方式。

2.3 build:构建管理面板

npm run build
# or
yarn build

构建(admin panel)管理后台前端产物,产出供 start 直接伺服的前端静态资源。

2.4 命令背后的脚本映射

package.jsonscripts 字段可以看到,上述三条 npm 脚本实际上是一比一映射到 Strapi CLI 的同名子命令:

{
  "scripts": {
    "build": "strapi build",
    "develop": "strapi develop",
    "start": "strapi start",
    "strapi": "strapi"
  }
}

其中额外的 "strapi": "strapi" 脚本允许通过 npm run strapi -- <子命令> 调用 CLI 的其他能力(如创建内容类型、初始化工程结构等)。从源码结构看,CLI 子命令的实现位于仓库的 packages/core/strapi 包中,该包即 @strapi/strapi 核心入口,负责解析参数、加载 config/ 下的配置文件并驱动应用生命周期;本文所介绍的三条命令均由该包提供的 strapi 可执行文件分发执行。

三、五个核心配置文件详解

示例工程将配置全部集中在 config/ 目录下,且每个文件都通过 import type { Core } from '@strapi/strapi' 引入 Strapi 官方类型,实现类型化配置——这是 TypeScript 示例相对 JavaScript 示例(如 examples/getstarted/config/)最显著的优势:配置文件获得完整的参数补全与类型检查。以下逐文件说明。

3.1 admin.ts:管理面板配置

config/admin.ts 完整内容如下:

import type { Core } from '@strapi/strapi';

const adminConfig = ({ env }: Core.Config.Shared.ConfigParams): Core.Config.Admin => ({
  auth: {
    secret: env('ADMIN_JWT_SECRET', 'example-token'),
  },
  apiToken: {
    salt: env('API_TOKEN_SALT', 'example-salt'),
  },
  transfer: {
    token: {
      salt: env('TRANSFER_TOKEN_SALT', 'example-salt'),
    },
  },
  flags: {
    nps: env.bool('FLAG_NPS', true),
    promoteEE: env.bool('FLAG_PROMOTE_EE', true),
    docLinks: env.bool('FLAG_DOC_LINKS', true),
  },
});

export default adminConfig;

关键参数:

  • auth.secret:管理员 JWT 的签名密钥,生产环境必须通过环境变量 ADMIN_JWT_SECRET 注入,示例中的 example-token 仅为占位值;
  • apiToken.salt:API Token 加盐哈希用的盐值;
  • transfer.token.salt:数据迁移(data transfer)令牌加盐值;
  • flags:若干功能开关,支持通过 FLAG_NPSFLAG_PROMOTE_EEFLAG_DOC_LINKS 环境变量覆盖,例如设置 FLAG_DOC_LINKS=false 可关闭后台文档外链。

该文件还演示了 Strapi 配置系统的一个核心机制:配置导出的是函数(接收 { env } 参数),而非静态对象。env 系列读取器定义在类型包的 packages/core/types/src/core/config/shared.ts 中,env() 读取字符串、env.bool() 读取布尔、env.int() 读取整数、env.array() 读取数组,第二个参数为默认值。这种设计使得同一份代码可以在不同部署环境(开发/生产)下通过环境变量差异化取值。

3.2 api.ts:REST API 行为配置

config/api.ts 配置 REST 接口的默认分页与响应行为:

const config: Core.Config.Api = {
  rest: {
    defaultLimit: 25,
    maxLimit: 100,
    withCount: true,
  },
};
  • defaultLimit: 25:列表接口未显式指定 pagination[limit] 时,每页默认返回 25 条;
  • maxLimit: 100:客户端可请求的每页条数上限;
  • withCount: true:响应中附带总条数统计,便于前端渲染分页器。

3.3 database.ts:数据库连接配置(SQLite)

config/database.ts 展示了 SQLite 连接配置的完整写法:

const config = ({ env }: Core.Config.Shared.ConfigParams): Core.Config.Database<'sqlite'> => {
  const connection: Core.Config.Database<'sqlite'>['connection'] = {
    client: 'sqlite',
    connection: {
      filename: path.join(__dirname, '..', '..', env('DATABASE_FILENAME', '.tmp/data.db')),
    },
    useNullAsDefault: true,
  };

  return {
    connection,
  };
};
  • client: 'sqlite' 与类型参数 Core.Config.Database<'sqlite'> 互相印证,类型系统按驱动区分了各数据库的连接结构;
  • 数据库文件路径通过 path.join(__dirname, '..', '..', ...) 锚定到工程根目录下的 .tmp/data.db,并允许用 DATABASE_FILENAME 环境变量覆盖;
  • useNullAsDefault: true 是 SQLite 驱动下的兼容性选项。

该文件同时说明:切换数据库(如 PostgreSQL、MySQL)时只需替换 clientconnection 结构,类型参数会随之变化,Strapi 的类型体系会给出对应的连接字段约束。

3.4 server.ts:服务与密钥配置

config/server.ts 控制 HTTP 服务监听与 Cookie 密钥:

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 / port:默认监听 0.0.0.0:1337,即 Strapi 的默认端口 1337;
  • app.keys:Cookie 签名密钥数组,支持多密钥轮换;示例值 toBeModified1/2 明确提示生产部署前必须修改为 APP_KEYS 环境变量的真实值。

3.5 middlewares.ts:中间件执行顺序

config/middlewares.ts 以字符串数组声明了请求处理的中间件链:

const config: Core.Config.Middlewares = [
  'strapi::logger',
  'strapi::errors',
  'strapi::security',
  'strapi::cors',
  'strapi::poweredBy',
  'strapi::query',
  'strapi::body',
  'strapi::session',
  'strapi::favicon',
  'strapi::public',
];

每个 strapi:: 前缀项指向核心内置中间件,数组顺序即 Koa 中间件的执行顺序:先记日志、统一错误处理与设置安全头,再处理 CORS、响应头、参数解析(query/body)、会话与静态资源。新增自定义中间件时,将其插入合适的位置即可改变其在请求生命周期中的执行时机。

四、应用入口与生命周期钩子

src/index.ts 是 Strapi 应用的服务端入口,导出两个异步钩子:

import '@strapi/strapi';

export default {
  register(/*{ strapi }*/) {},
  bootstrap(/*{ strapi }*/) {},
};
  • register:在应用初始化之前运行,用于扩展内部代码逻辑(如注册全局服务、修改初始化流程);
  • bootstrap:在应用启动完成之前运行,适合初始化数据模型、跑定时任务或执行特殊启动逻辑。

两个钩子均以 { strapi } 实例为参数(示例中注释掉了参数),可以在其中访问完整的 Strapi 实例 API。文件首行的 import '@strapi/strapi' 用于引入核心的副作用初始化逻辑。

五、管理面板扩展:locales 与前端 bootstrap

管理后台的定制代码放在 src/admin/ 目录。示例文件 app.example.tsx 演示了两类管理端扩展点:

import type { StrapiApp } from '@strapi/strapi/admin';

export default {
  config: {
    locales: [
      // 'fr', 'de', 'es', 'ja', 'zh-Hans', 'zh', ...(示例中注释了可选语言列表)
    ],
  },
  bootstrap(app: StrapiApp) {
    console.log(app);
  },
};
  • config.locales:声明后台界面支持的语言列表,取消注释即可启用对应 locale;
  • bootstrap(app: StrapiApp):管理前端应用初始化时执行,参数是类型化的 StrapiApp 实例,可用于注入全局组件、覆写路由或修改面板行为。

同目录下的 webpack.config.example.ts 则预留了自定义构建管线的示例位置。需要留意的是,这些文件以 .example 命名,属于示例性质:实际启用扩展时需按 Strapi 的 admin 扩展约定将其落为正式入口文件后再构建。

六、双 tsconfig 设计:后端 CommonJS 与前端 ESNext

该示例工程存在两套独立的 TypeScript 配置,分别服务前后端两种构建目标,这是理解 Strapi TS 项目构建机制的关键。

6.1 后端(服务端)tsconfig

根目录 tsconfig.json 面向运行在 Node.js 中的服务端代码:

{
  "compilerOptions": {
    "outDir": "dist",
    "rootDir": ".",
    "module": "CommonJS",
    "moduleResolution": "Node",
    "lib": ["ES2020"],
    "target": "ES2019",
    "strict": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true,
    "incremental": true,
    "esModuleInterop": true,
    "resolveJsonModule": true,
    "noEmitOnError": true,
    "noImplicitThis": true
  },
  "include": ["./", "src/**/*.json"],
  "exclude": [
    "node_modules/", "build/", "dist/", ".cache/", ".tmp/",
    ".strapi/", "src/admin/", "**/*.test.ts", "src/plugins/**"
  ]
}

要点:

  • module: CommonJS + moduleResolution: Node:服务端产物为 CommonJS,与 Node 运行时匹配;
  • target: ES2019lib: ES2020:语言特性基线;
  • noEmitOnError: true 配合 incremental: true:类型错误即阻断产物输出,且增量编译提升二次构建速度;
  • exclude 中显式排除了 src/admin/——前端代码不进入这套编译目标。

6.2 管理前端 tsconfig

src/admin/tsconfig.json 则面向由 webpack/bundler 打包的前端代码:

{
  "compilerOptions": {
    "target": "ESNext",
    "module": "ESNext",
    "moduleResolution": "Bundler",
    "useDefineForClassFields": true,
    "lib": ["DOM", "DOM.Iterable", "ESNext"],
    "allowJs": false,
    "jsx": "react-jsx",
    "noEmit": true,
    "strict": true
  },
  "include": ["../plugins/**/admin/src/**/*", "./"],
  "exclude": ["node_modules/", "build/", "dist/", "**/*.test.ts"]
}

要点:module: ESNext + moduleResolution: Bundler 说明前端模块由打包器解析;jsx: react-jsx 匹配 React 18 的自动 JSX 运行时;noEmit: true 表示它只做类型检查,产物交由 strapi build 的 webpack 管线生成。include 同时纳入了 ../plugins/**/admin/src/**/*,即本地插件的 admin 源码也参与这套前端检查。

这种"一个工程、两套 tsconfig"的划分,与 package.jsonreactreact-domreact-router-domstyled-components 四个前端依赖共存于后端依赖列表的做法相互印证:Strapi 示例工程将前后端依赖合并声明,由构建流程按目录边界区分用途。

七、运行环境与依赖约束

package.json 明确声明了运行环境要求,在部署前必须满足:

"engines": {
  "node": ">=20.0.0 <=26.x.x",
  "npm": ">=6.0.0"
}

即 Node.js 版本需落在 >=20.0.0 且不超过 26.x.x 区间。其他关键依赖及其作用:

依赖 版本 用途
@strapi/strapi workspace:* 核心框架(含 CLI、服务、管理面板)
@strapi/plugin-users-permissions workspace:* 用户与权限插件(注册、登录、Token)
better-sqlite3 12.8.0 SQLite 数据库驱动,对应 database.tsclient: 'sqlite'
react / react-dom 18.3.1 管理面板前端运行时
react-router-dom 6.30.4 管理面板路由
styled-components 6.4.1 管理面板样式方案

数据库方面,示例默认使用 SQLite(文件落盘在工程根目录的 .tmp/data.db),开箱即用无需外部服务;如需切换到 PostgreSQL/MySQL 等,按 3.3 节的 database.ts 模式替换连接配置,并在 dependencies 中增加对应驱动(仓库其他示例工程如 examples/complex/scripts/ 下即提供了面向 Postgres/MySQL/MariaDB/SQLite 的多数据库调试脚本,可作参照)。

八、部署建议

README 的 Deployment 部分指出 Strapi 提供多种部署选项。基于本文分析的项目结构,一条最小可行的部署路径为:

  1. 通过环境变量注入生产密钥:ADMIN_JWT_SECRETAPI_TOKEN_SALTAPP_KEYS,并将 DATABASE_FILENAME 指向持久化存储位置(若使用外部数据库则替换 database.ts 连接);
  2. 执行 npm run build 构建管理面板前端产物;
  3. 执行 npm run start 以禁用 autoReload 的模式启动服务,默认监听 0.0.0.0:1337,可用 HOST / PORT 覆盖;
  4. 静态上传文件默认写入工程内 public/uploads/ 目录,部署时需保证该目录可写并持久化,或改用仓库 examples/complex/config 中演示的对象存储类 provider 方案。

九、小结

examples/kitchensink-ts 以最小的文件集合完整演示了 Strapi TypeScript 项目的标准形态:三条 CLI 命令(develop / start / build)经由 package.json 的脚本映射到 strapi 子命令;config/ 下五个类型化配置文件覆盖管理面板、REST API、数据库、服务参数与中间件链;src/index.tssrc/admin/app.example.tsx 分别提供前后端两类生命周期扩展点;双 tsconfig 设计划定了 CommonJS 服务端与 ESNext 前端两条构建边界。将本文各节的配置与命令组合使用,即可把一个空的 Strapi TS 工程从零启动、定制到部署。

登录后查看全文
热门项目推荐
相关项目推荐