拓冰建站拓冰建站
首页 / 资讯中心 / 正文

Backstage 插件开发黄金路径:从脚手架到前后端生产级插件实战指南

Backstage 插件开发黄金路径从脚手架到前后端生产级插件实战指南【免费下载链接】backstageBackstage is an open framework for building developer portals项目地址: https://gitcode.com/GitHub_Trending/ba/backstage本篇技术指南以 Backstage 官方 golden path 系列中的 插件开发章节 为主体骨架完整覆盖为什么要写插件—如何用 CLI 脚手架后端/前端插件—如何持久化数据—如何联调 HTTP API—如何测试的完整链路。读完本文你将能独立用yarn new创建并交付一个可运行、可持久化、有测试保障的 Backstage 前后端插件并掌握仓库中真实源码的对应实现如 example-todo-list-backend作为对照参考。一、插件在 Backstage 中的价值定位Backstage 官方插件教程 why-build-plugins.md 指出插件是 Backstage 生态的核心构件它让各种工具和服务能够集成进统一开发者门户。插件解决的真实场景往往来自一线开发者日常的toil苦差事浪费时间手动编译充满易错数据的电子表格每周花数小时寻找某个特定网站上的某条特定链接以及成千上万影响开发者流程的其他问题。插件的核心价值价值维度说明提升开发者生产力插件集中化、简化工具访问减少开发者在多个系统间切换的时间通过一致的界面与交互降低认知负担让开发者专注编码而非工具管理可定制、可扩展的平台Backstage 插件架构高度灵活可集成几乎所有基础设施或软件开发工具支持组织按需定制协作与知识共享将文档、代码仓库、CI/CD 流水线、监控工具整合在一处团队成员更容易找到信息、分享洞察一致性与最佳实践遵循 Backstage 设计规范如 structure-of-a-plugin、create-a-plugin的插件能保证全平台一致的体验降低新用户学习曲线可扩展性与可维护性插件设计为模块化、独立可独立升级维护、独立水平扩展二、场景设定公司黑客松上的 Todo 追踪器教程设定了一个贯穿全文的实战场景index.md你有一个绝妙想法要在公司黑客松上为你的 Backstage 实例创建一个Todo 列表追踪器。Backstage 本应统一我们所有信息理应也能追踪未来待办任务。本黄金路径的整体学习路径为先做后端插件接触 HTTP API、数据库与 Backstage 后端系统new backend system再做前端插件创建对用户可见的新页面并调用自己的 API最后介绍常见集成软件目录catalog、搜索search、权限permissions、通知notifications等。三、可持续的插件开发方法论在动手写代码前sustainable-plugin-development.md 强调插件不是凭空产生的它通常解决某个客户诉求——业务问题如展示云成本、新集成如展示 PagerDuty 等外部厂商数据、或开发者痛点如整合零散系统的信息。找到你的干系人你的内部开发团队就是你的客户识别受影响团队做云成本插件就找云基础设施团队做值班信息整合就找事故响应流程的负责团队倾听要点痛点哪些手工步骤拖慢他们、频率每日挫折比季度问题更值得投入、现有变通方案往往揭示最小可行方案需要覆盖什么次要干系人平台团队、工程经理、团队负责人能提供组织级视角也能成为插件发布后的采用推广大使。何时迭代插件常见迭代触发信号干系人反馈暴露缺口团队在用你的插件但仍为某件具体的事切换到别的工具采用率低于预期先弄清原因问题往往出在可发现性、缺少上下文或工作流不匹配而非功能缺失底层数据或服务变更外部系统在演进与拥有它的团队保持沟通线分析数据揭示意外模式Backstage 内置 analytics 事件支持埋点后使用数据会揭示哪些部分被重度使用、哪些被忽略、用户在哪里流失。插件成功的基石保持干系人关系活跃、让反馈驱动优先级、为插件埋点instrument、把它当产品而非项目来经营——一个上线即被弃用的插件会迅速失去用户信任即使是小而规律的改进也表明该插件被维护、值得依赖。四、创建后端插件用yarn new脚手架4.1 执行脚手架命令后端插件教程 backend/001-first-steps.md 详细描述了创建过程。在 Backstage 仓库根目录执行yarn new交互式选择backend-plugin。系统会要求你提供插件名称——这个标识符将成为 NPM 包名的一部分所以应简短、全小写、用连字符分隔。本教程示例提供todo若未来某个插件集成名为 Carmen 的系统可命名为carmen。这将创建一个类似internal/plugin-carmen-backend的 NPM 包具体名字取决于传给new命令的其他参数以及根目录package.json中new命令的配置。创建过程会运行初始安装与构建命令请耐心等待。关于new命令的更多细节可参考 CLI 文档 new 模块其中说明了命令通常被配置为根package.json的脚本{ scripts: { new: backstage-cli new } }支持--select预选模板、--option传入选项例如yarn backstage-cli new --select frontend-plugin --option pluginIdfoo。4.2 脚手架产物结构命令完成后你会看到新文件夹plugins/todo-backend结构如下/ - your Backstage apps root directory /plugins/ /todo-backend/ package.json README.md eslintrc.js /dev/ index.ts /src/ plugin.ts index.ts router.ts /services/ /TodoListService/ TodoListService.ts types.ts index.ts仓库中可对照的真实实现位于 plugins/example-todo-list-backend其exampleTodoListPlugin通过createBackendPlugin定义插件在register(env)中注册env.registerInit初始化时依赖httpAuth、logger、httpRouter三个核心服务将路由挂载到httpRouter并为/health路径添加allow: unauthenticated的认证策略export const exampleTodoListPlugin createBackendPlugin({ pluginId: todolist, register(env) { env.registerInit({ deps: { httpAuth: coreServices.httpAuth, logger: coreServices.logger, httpRouter: coreServices.httpRouter, }, async init({ httpAuth, logger, httpRouter }) { httpRouter.use(await createRouter({ httpAuth, logger })); httpRouter.addAuthPolicy({ path: /health, allow: unauthenticated, }); }, }); }, });路由实现见 service/router.ts它用express-promise-router创建路由器通过httpAuth.credentials(req, { allow: [user] })校验用户凭证GET /todos列出、POST /todos创建带InputError载荷校验。4.3 默认插件的功能与本地测试backend/002-poking-around.md 指出默认创建的插件自带一个简单的 Todo 列表应用暴露 HTTP API 于http://localhost:7007/api/todo/todos支持创建 Todo、列出 Todo、获取单个 Todo并且将 Todo 存在内存中重启即丢失还支持用软件目录实体给 Todo 打标签便于后续前端集成。要让插件生产就绪需要做三件事把 Todo 写入数据库重启不丢失编写恰当的测试保证一切符合预期获取用户反馈。本地运行打开plugins/todo-backend/package.json的scripts段两个关键命令yarn start—— 以dev/index.ts内容作为后端启动本地开发服务器yarn test—— 运行后端插件的全部测试。运行yarn start后关注日志2025-06-08T16:14:53.229Z rootHttpRouter info Listening on :7007这表示 HTTP 服务器已启动可以发送测试请求了。从干净状态开始列出所有 Todo应返回空列表curl http://localhost:7007/api/todo/todos创建 Todo直接 POST 会得到401因为插件要追踪创建 Todo 的用户 IDcurl -X POST http://localhost:7007/api/todo/todos \ -H Content-Type: application/json; charsetutf-8 \ --data-binary - EOF { title: My Todo } EOF带上凭证创建有前端配套的插件会自动处理凭证管理curl -v -X POST http://localhost:7007/api/todo/todos \ -H Content-Type: application/json; charsetutf-8 \ -H Authorization: Bearer $(curl -s http://localhost:7007/api/auth/guest/refresh | jq -r .backstageIdentity.token) \ --data-binary - EOF { title: My Todo } EOF再次列出时你会看到createdBy为user:development/guest——这正是令牌部分的作用。五、为插件添加数据库持久化5.1 持久化动机与 SQLite 简介backend/003-persistence.md 指出重启 Backstage 后端后 Todo 列表会消失。在运行yarn start的终端按ENTER即可强制重启后端内存数据会全部清空——但数据库里的数据不会。SQLite 是本地开发默认数据库跑在内存中也可以落盘为文件迭代周期快出问题可轻松删除。5.2 数据在库中的形态我们的 Todo 对象含title、id、createdBy、createdAt四个键非常适合与数据库表 1:1 映射。5.3 接入databaseService第一步接线plumbing给服务工厂加上database: coreServices.database依赖TodoListService.ts 改造示意export const todoListServiceRef createServiceRefExpandTodoListService({ id: todo.list, defaultFactory: async service createServiceFactory({ service, deps: { logger: coreServices.logger, catalog: catalogServiceRef, database: coreServices.database, }, async factory(deps) { return TodoListService.create(deps); }, }), });接着把DatabaseService与Knex类型引入服务类用await options.database.getClient()获取 knex 客户端并保存为readonly #database。这样我们就有了一个与数据库通信的独立 knex 客户端。第二步创建数据表migrationknex 把迁移存储为 JS/TS 文件在调用knex.migrate.latest()时执行默认放在migrations/目录。先安装依赖并生成迁移文件yarn workspace internal/plugin-todo-backend add knex yarn workspace internal/plugin-todo-backend knex migrate:make init --migrations-directory ./migrations生成类似Created Migration: .../plugins/todo-backend/migrations/20260323130057_init.js的文件包含up应用迁移与down撤销迁移两个可逆函数。填入建表逻辑注意 SQL 惯例使用snake_caseexports.up async function up(knex) { await knex.schema.createTable(todo, table { table.uuid(id).primary(); table.string(created_by, 255).notNullable(); table.string(title).notNullable(); table.datetime(created_at).defaultTo(knex.fn.now()).notNullable(); table.index([created_by], todo_user_idx); }); };exports.down async function down(knex) { await knex.schema.dropTable(todo); };然后在插件init函数中让 knex 客户端自动应用迁移async init({ httpAuth, logger, httpRouter, database, todoList }) { const knex await database.getClient(); if (!database.migrations?.skip) { logger.info(Running database migrations...); const migrationsDir resolvePackagePath( internal/plugin-todo-backend, migrations, ); await knex.migrate.latest({ directory: migrationsDir, }); } httpRouter.use(await createRouter({ httpAuth, todoList })); }这里三点值得注意database.migrations?.skip—— 通过配置跳过迁移的约定resolvePackagePath—— 无论何种环境都确保传入正确的迁移目录await knex.migrate.latest()—— 实际运行迁移调用上面写的up方法。最后务必在package.json的files数组中加上migrations否则发布后迁移文件不会随包分发files: [ - dist dist, migrations ],5.4 定义数据库行类型与双向转换为抵御运行时类型不兼容手写数据库行类型snake_case 需与库表 schema 一致并实现TodoItem↔TodoDatabaseRow的转换export interface TodoDatabaseRow { title: string; id: string; created_by: string; created_at: string; } export interface TodoItem { title: string; id: string; createdBy: string; createdAt: string; } // toDatabaseRow: TodoItem - TodoDatabaseRow写入用 // fromDatabaseRow: TodoDatabaseRow - TodoItem读取用5.5 写入与读取创建 Todo 改为插入数据库async createTodo(/* ... */) { const id crypto.randomUUID(); const createdBy options.credentials.principal.userEntityRef; const newTodo { title, id, createdBy, createdAt: new Date().toISOString(), }; - this.#storedTodos.push(newTodo); await this.#database .insert(this.toDatabaseRow(newTodo)) .into(todo); return newTodo; }读取 Todo 改为查询数据库async listTodos(): Promise{ items: TodoItem[] } { - return { items: Array.from(this.#storedTodos) }; const rows await this.#database(todo).select(); return { items: rows.map(row this.fromDatabaseRow(row)) }; } async getTodo(request: { id: string }): PromiseTodoItem { - const todo this.#storedTodos.find(item item.id request.id); const item await this.#database(todo).where({ id: request.id }).first(); - if (!todo) { if (!item) { throw new NotFoundError(No todo found with id ${request.id}); } - return todo; return this.fromDatabaseRow(item); }验证方式与上一节相同——如果一切正常你会得到与之前一致的响应但数据现在跨重启保留了。六、前端插件创建用户可见的页面6.1 脚手架前端插件frontend/001-first-steps.md 说明在仓库根目录执行yarn new --select frontend-plugin --option pluginIdtodo --option owner这会创建类似internal/plugin-todo的 NPM 包新文件夹出现在plugins/todo结构如下plugins/todo/ ├── dev/ # Standalone dev server setup ├── src/ │ ├── components/ │ │ ├── TodoList/ │ │ └── TodoPage/ │ └── ... # Plugin definition, routes, tests └── package.json6.2 脚手架产物的关键文件文件作用src/plugin.tsx主插件定义用createFrontendPlugin创建插件、用PageBlueprint注册页面扩展src/plugin.test.ts插件定义测试验证插件及其扩展创建正确src/routes.ts路由引用定义用于插件间导航src/index.ts包入口默认导出插件src/components/TodoPage/主页面组件从后端抓取 todo 并用TodoList渲染src/components/TodoList/展示型组件用backstage/ui渲染 todo 表格dev/index.tsx独立开发应用只加载你的插件在插件目录运行yarn start启动package.json注意backstage.role字段为frontend-plugin告知工具链如何构建与对待该包6.3 验证与常见问题若应用启用了功能发现feature discovery默认开启插件会被自动拾取否则参考 安装插件文档 手动添加到应用。在仓库根目录yarn start浏览器访问http://localhost:3000/todo路径与插件 ID 对应会看到带页头和示例数据的 todo 页面若后端 todo 插件也在运行页面显示真实 todo 数据。独立运行yarn workspace internal/plugin-todo start。常见问题排查页面不显示确认app-config.yaml中app.packages为all若使用 include/exclude 过滤确保插件包未被排除yarn new安装失败先在仓库根目录执行yarn install并确认 Node.js 版本匹配项目要求脚手架后 TypeScript 报错在仓库根目录运行yarn tsc检查类型错误全新脚手架应能干净编译否则重新yarn install。七、读懂脚手架生成的前端代码frontend/002-poking-around.md 逐文件讲解了脚手架生成的代码。7.1 插件定义plugins/todo/src/plugin.tsx是插件入口import { createFrontendPlugin, PageBlueprint, } from backstage/frontend-plugin-api; import { rootRouteRef } from ./routes; export const page PageBlueprint.make({ params: { path: /todo, routeRef: rootRouteRef, loader: () import(./components/TodoPage).then(m m.TodoPage /), }, }); export const todoPlugin createFrontendPlugin({ pluginId: todo, extensions: [page], routes: { root: rootRouteRef, }, });要点createFrontendPlugin把插件注册进 BackstagePageBlueprint.make定义一个页面扩展——应用中一条懒加载TodoPage组件的路由rootRouteRef是路由引用其他插件可用它链接到你的插件页面。7.2 TodoPage 组件与 TodoList 组件TodoPage.tsx中const { value: todos, loading, error } useTodos();用fetchApiRef请求plugin://todo/todos。fetchApiRef包装了浏览器fetch自动注入认证凭证并把plugin://URL scheme 解析为正确的后端插件端点例如http://localhost:7007/api/todo/todos。若后端未运行页面回退到示例数据保证开箱即用。TodoList.tsx是纯展示组件接收 todos 列表作为 props 并用backstage/ui的Table渲染。TodoItem类型与后端插件返回的形状一致export type TodoItem { title: string; id: string; createdBy: string; createdAt: string; };7.3 页面结构理解脚手架插件使用backstage/ui与backstage/core-components的组件保持一致观感页面顶部栏通常由外围的PageLayout默认应用里通常是PluginHeader提供而非页面组件内的自定义HeaderContainer是页面主内容区来自backstage/uiTable渲染带列配置的数据表来自backstage/uiProgress显示加载指示器来自backstage/core-components。让插件与 Backstage 其余部分视觉一致很重要——无论用户在使用哪个插件都应感到熟悉。八、前端 HTTP 客户端调用后端 APIfrontend/004-http-client.md 详解了脚手架TodoPage如何取数及如何扩展。8.1 脚手架代码如何工作useTodoshook 使用useApi(fetchApiRef)function useTodos() { const { fetch } useApi(fetchApiRef); return useAsync(async (): PromiseTodoItem[] { const response await fetch(plugin://todo/todos); if (!response.ok) { throw new Error( Failed to fetch todos: ${response.status} ${response.statusText}, ); } const data await response.json(); return data.items; }); }fetchApi自动做两件事注入认证凭证——无需手动附加任何Authorization头把plugin://pluginIdURL scheme 解析为实例的真实插件 URL。useAsync来自react-hookz/web在挂载时运行异步函数返回[{ status, result, error }, { execute }]组件据此显示加载动画、后端请求失败时显示示例 todo、或显示取到的 todo 列表。8.2 实际联调前后端都运行时仓库根目录yarn start同时启动两者访问http://localhost:3000/todo即可看到后端返回的 todos。小技巧可先按后端黄金路径用 curl 创建 todos再刷新前端页面即可看到。8.3 抽取客户端类当插件有多个端点时抽取独立客户端类可让组件专注渲染。创建plugins/todo/src/api/TodoClient.tsimport { FetchApi } from backstage/frontend-plugin-api; import type { TodoItem } from ../components/TodoList; export class TodoClient { readonly #fetchApi: FetchApi; constructor(options: { fetchApi: FetchApi }) { this.#fetchApi options.fetchApi; } async listTodos(): PromiseTodoItem[] { const response await this.#fetchApi.fetch(plugin://todo/todos); if (!response.ok) { throw new Error( Failed to fetch todos: ${response.status} ${response.statusText}, ); } const data await response.json(); return data.items; } async createTodo(title: string): PromiseTodoItem { const response await this.#fetchApi.fetch(plugin://todo/todos, { method: POST, headers: { Content-Type: application/json }, body: JSON.stringify({ title }), }); if (!response.ok) { throw new Error( Failed to create todo: ${response.status} ${response.statusText}, ); } return response.json(); } }对脚手架示例这步可选但插件成长后价值会显现。8.4 OpenAPI 生成客户端还可以通过 OpenAPI schema 生成客户端让前后端保持同步若后端插件暴露了 OpenAPI spec即可生成类型安全客户端API 变更时自动更新降低前后端随时间漂移的风险。九、测试与质量保障后端教程 backend/005-testing.md 强调此前我们做了大量手工验证应把这些假设固化为可随每次变更运行的代码。测试层次包括router 级测试、plugin 级测试、OpenAPI 测试含 Jest 集成与模糊测试。仓库中的真实测试实践可参考 example-todo-list-backend 的测试文件 以及通用测试工具 backend-test-utils。后端元信息文档 backend/meta.md 也给出了最小单元测试思路用supertest断言每个路由行为符合预期例如用httpAuth.credentials校验后返回 401 的场景。十、生产就绪与后续集成方向10.1 从黑客松原型到生产插件回到最初场景默认插件要生产就绪需完成三件事——数据持久化本文第五节已完成、编写测试、收集用户反馈结合第三节方法论持续迭代。10.2 常见平台集成本黄金路径还预留了四个常见的集成专题见 integrations 目录catalog软件目录让插件读取/写入实体数据search搜索把插件内容接入全局搜索permissions权限基于 Backstage 权限模型控制插件功能notifications通知向用户推送系统内通知。仓库中已有可参考的成熟实现例如 notifications 插件 与 search 后端可作为深入研读的样板。十一、学习总结与进一步阅读通过这条黄金路径你已掌握为什么要构建 Backstage 插件统一门户、消除 toil、社区生态如何用yarn new脚手架后端插件与前端插件理解每一份生成文件的职责如何为后端插件接入databaseService、编写 knex migration 并实现真正的持久化如何在前端用fetchApiRefplugin://URL 调用后端 API并抽取类型安全的客户端类如何用测试固化行为、规划可持续的插件演进路线。深入阅读路径插件结构规范见 structure-of-a-plugin创建插件全流程见 create-a-plugin新后端系统原理见 new-backend-system插件测试指南见 testing。【免费下载链接】backstageBackstage is an open framework for building developer portals项目地址: https://gitcode.com/GitHub_Trending/ba/backstage创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门