--- name: backstage-backend-plugin description: "Build Backstage backend plugins with createBackendPlugin and core services: DI, httpRouter, secure-by-default auth, Knex DB, routes, testing. Use for APIs and background jobs." version: 1.0.0 license: Complete terms in LICENSE.txt --- # Backstage Backend Plugin (New Backend System) ## About This Skill This skill provides specialized knowledge and workflows for building Backstage backend plugins using the New Backend System. It guides the development of server-side functionality including REST/HTTP APIs, background jobs, data processing, and integrations. ### What This Skill Provides 1. **Specialized workflows** - Step-by-step procedures for creating and configuring backend plugins 2. **Best practices** - Production-ready patterns for auth, validation, error handling, and database usage 3. **Golden path templates** - Copy/paste code snippets for common backend patterns 4. **Domain expertise** - Backstage-specific knowledge about DI, core services, and secure-by-default architecture ### When to Use This Skill Use this skill when creating server-side functionality for Backstage: REST/HTTP APIs, background jobs, data processing, or integrations. --- # Development Workflow ## Phase 1: Planning and Research ### 1.1 Understand the Requirements Before building a backend plugin, clearly understand: - What endpoints or APIs are needed (REST, GraphQL, webhooks) - What data storage requirements exist (database, cache) - Whether background jobs or scheduled tasks are needed - Authentication and authorization requirements - Integration points with other services or plugins ### 1.2 Load Reference Documentation Load reference files as needed based on the plugin requirements: **For Core Services:** - [⚙️ Core Services Reference](./reference/core_services.md) - Comprehensive guide to all core backend services including: - httpRouter, logger, database, httpAuth, userInfo - cache, scheduler, urlReader, discovery, permissions - Usage patterns and best practices for each service **For Testing:** - [✅ Testing Reference](./reference/testing.md) - Comprehensive testing guide for backend plugins --- ## Phase 2: Implementation Follow the Golden Path workflow below for implementation, referring to reference files as needed. **Important Decisions:** - Determine which core services are needed (load [⚙️ Core Services Reference](./reference/core_services.md)) - Plan database schema and migrations if using database - Design authentication policies (which endpoints need auth?) - Plan error handling and validation strategies --- ## Phase 3: Testing After implementing the plugin: 1. Load the [✅ Testing Reference](./reference/testing.md) 2. Write comprehensive tests for: - Router endpoints using `startTestBackend` - Database operations with `TestDatabases` - External service calls with MSW (Mock Service Worker) - Authentication flows 3. Run tests and achieve good coverage: ```bash yarn backstage-cli package test --coverage ``` --- ## Phase 4: Review and Polish Before publishing: 1. Run linting and structure checks 2. Ensure proper error handling throughout 3. Verify authentication policies are correct 4. Check database migrations are production-ready 5. Review the Common Pitfalls section below 6. Test horizontal scalability (no in-memory state) --- ## Quick Facts - Scaffold with `yarn new` → backend‑plugin; the package lives in `plugins/-backend/`. - Define the plugin with `createBackendPlugin`, declare dependencies via `deps`, and initialize in `register(env).registerInit`. - Attach routes through `coreServices.httpRouter`. Backstage prefixes plugin routers with `/api/`. - Backends are secure by default; use `httpRouter.addAuthPolicy({ path, allow })` to allow unauthenticated endpoints like `/health`. - Core services (logger, database, httpRouter, httpAuth, userInfo, urlReader, scheduler, etc.) are available via `coreServices`. --- ## Production Best Practices ### Request Validation Validate inputs at the edge using a schema (e.g., zod) before hitting DBs or external services: ```ts import { z } from 'zod'; const querySchema = z.object({ q: z.string().min(1) }); router.get('/search', async (req, res, next) => { const parsed = querySchema.safeParse(req.query); if (!parsed.success) return res.status(400).json({ error: 'invalid query' }); try { // ... perform work with parsed.data.q res.json({ items: [] }); } catch (e) { next(e); } }); ``` ### Error Handling Add a terminal error handler to your router and prefer structured logs with context: ```ts import { errorHandler } from '@backstage/backend-common'; router.use(errorHandler()); ``` ### Auth and Identity - Keep backends secure-by-default - Open only explicit paths with `addAuthPolicy` - For protected routes, extract credentials with `httpAuth` - Derive user/entity identity via `userInfo` when required ```ts // Inside a route handler const creds = await httpAuth.credentials(req, { allow: ['user', 'service'] }); const { userEntityRef } = await userInfo.getUserInfo(creds); logger.info('request', { userEntityRef }); ``` ### Database Usage - Use `const knex = await database.getClient();` to get a database client - Keep queries in small repo/service modules - Write migrations in JavaScript (`.js`) and export them in package.json (see Core Services Reference for details) ### Observability & Scalability - Avoid in-memory state - Make handlers idempotent - Log with `logger.child({ plugin: 'example' })` for traceability --- ## Golden Path (Copy/Paste Workflow) ### 1) Scaffold ```bash # From the repository root (interactive) yarn new # Select: backend-plugin # Plugin id (without -backend suffix), e.g. example # Non-interactive (for AI agents/automation) yarn new --select backend-plugin --option pluginId=example --option owner="" ``` This creates `plugins/example-backend/` using the New Backend System with `createBackendPlugin`. ([Backstage][3]) ### 2) `src/plugin.ts` — plugin + DI + router ```ts import { createBackendPlugin, coreServices } from '@backstage/backend-plugin-api'; import { createRouter } from './service/router'; export const examplePlugin = createBackendPlugin({ pluginId: 'example', register(env) { env.registerInit({ deps: { httpRouter: coreServices.httpRouter, logger: coreServices.logger, database: coreServices.database, // optional httpAuth: coreServices.httpAuth, // optional userInfo: coreServices.userInfo, // optional }, async init({ httpRouter, logger, database, httpAuth, userInfo }) { const router = await createRouter({ logger, database, httpAuth, userInfo }); httpRouter.use(router); // Secure-by-default: open /health only httpRouter.addAuthPolicy({ path: '/health', allow: 'unauthenticated' }); }, }); }, }); export { examplePlugin as default } from './plugin'; ``` Key points: DI via `deps`, register routes with the plugin’s `httpRouter`, and export the plugin as the default. ([Backstage][3]) ### 3) `src/service/router.ts` — minimal Express router ```ts import express from 'express'; import type { LoggerService, DatabaseService, HttpAuthService, UserInfoService, } from '@backstage/backend-plugin-api'; export interface RouterOptions { logger: LoggerService; database?: DatabaseService; httpAuth?: HttpAuthService; userInfo?: UserInfoService; } export async function createRouter(options: RouterOptions): Promise { const { logger } = options; const router = express.Router(); router.get('/health', (_req, res) => { logger.info('health check'); res.json({ status: 'ok' }); }); return router; } ``` ### 4) Add to your backend In `packages/backend/src/index.ts`: ```ts const backend = createBackend(); backend.add(import('@internal/plugin-example-backend')); backend.start(); ``` Now `GET http://localhost:7007/api/example/health` returns `{ "status": "ok" }`. ([Backstage][3]) ### 5) Database, auth, identity (when needed) - Database: depend on `coreServices.database` to get a Knex client; create your own migrations and run them via your chosen process. ([Backstage][3]) - Identity: use `coreServices.httpAuth` + `coreServices.userInfo` to obtain the calling user and their entity refs when an endpoint needs identity. ([Backstage][3]) - Core services catalog: consult the Backstage Core Backend Service APIs for the full list (cache, scheduler, urlReader, etc.). ([Backstage][4]) --- ## Verify in a Backstage backend - Add the plugin to `packages/backend/src/index.ts` via `backend.add(import('@internal/plugin--backend'))`. - Start the repo (e.g., `yarn start` at the root). Then check: - GET http://localhost:7007/api/example/health → `{ "status": "ok" }` - If 401 occurs, ensure you opened `/health` with `addAuthPolicy`. ## Testing, linting & structure checks Use Backstage's CLI for tests and lints: ```bash yarn backstage-cli package test yarn backstage-cli package lint yarn backstage-cli repo lint ``` Keep routers small (`/service/router.ts`), inject dependencies (DB, auth, clients) from `plugin.ts`, and avoid in-memory state (horizontally scalable). --- ## Common Pitfalls (and Fixes) | Problem | Solution | Reference | | ------- | -------- | --------- | | **404s under `/api`** | Remember Backstage prefixes plugin routers with `/api/` | [Backstage][3] | | **Auth unexpectedly required** | Backends are secure by default; open endpoints explicitly via `httpRouter.addAuthPolicy` | [Backstage][3] | | **Tight coupling** | Never call other backend code directly; communicate over the network or through well-defined services | [Backstage][3] | --- # Reference Files ## 📚 Documentation Library Load these resources as needed during development: ### Core Services - [⚙️ Core Services Reference](./reference/core_services.md) - Complete guide to all core backend services including: - HTTP Router Service for route registration - Logger Service for structured logging - Database Service for Knex-based data access - HTTP Auth Service and User Info Service for authentication - Cache Service for key-value storage - Scheduler Service for background tasks - Discovery Service for inter-plugin communication - URL Reader Service for reading external content - Permissions Service for authorization - Configuration and health check services - Service composition examples and best practices ### Testing - [✅ Testing Reference](./reference/testing.md) - Comprehensive testing guide including: - Testing backend plugins with `startTestBackend` - Mock services for all core services - Testing routers with supertest - Testing authentication and permissions - External service mocking with MSW - Database testing with `TestDatabases` - Service factory testing - Integration testing patterns - Best practices and common patterns --- ## External References - Backend plugins: scaffolding, DI, `httpRouter`, `/api/`, secure‑by‑default, DB & identity usage. ([Backstage][3]) - Core Backend Service APIs index (logger, database, httpAuth, scheduler, urlReader, etc.). ([Backstage][4]) - Anthropic Skill spec & packaging details (required metadata; ≤200 char description). ([Claude Help Center][2]) [2]: https://support.claude.com/en/articles/12512198-creating-custom-skills [3]: https://backstage.io/docs/plugins/backend-plugin/ [4]: https://backstage.io/docs/backend-system/core-services/index/