diff --git a/docs/openapi/01-getting-started.md b/docs/openapi/01-getting-started.md new file mode 100644 index 0000000000..eb3b1c1209 --- /dev/null +++ b/docs/openapi/01-getting-started.md @@ -0,0 +1,115 @@ +--- +id: 01-getting-started +title: Schema-first plugins with OpenAPI (Experimental) +description: Tutorial on how to start using OpenAPI schema-first development in your plugins. +--- + +# Getting started with OpenAPI in your Backstage plugins + +Target Audience: Plugin developers + +Difficulty: Medium + +## Goal + +The goal of this tutorial is to give you exposure to tools that more tightly couple your OpenAPI specification and plugin lifecycle. The tools we'll be presenting were created by the OpenAPI tooling project area and allow you to create, + +1. A typed `express` router that provides strong guardrails during development for input and output values. Support for query, path parameters and request body, as well as experimental support for headers and cookies. +2. An auto-generated client to interact with your plugin's backend. Support for all request types, parameters and body, as well as return types. Provides a low-level interface to allow more customization by higher level libraries. +3. Validation and verification tooling to ensure your API and specification stay in sync. Includes testing against your unit tests. + +## Prerequisites + +### Technical Knowledge + +This tutorial assumes that you're already familiar with the following, + +1. How to build a Backstage plugin. +2. `Express.js` and `Typescript` +3. OpenAPI 3.0 schemas + +### Setting up + +There are two required npm packages before we start, + +1. `@backstage/repo-tools`, this package contains all OpenAPI related commands for your plugins. We will be using this throughout the tutorial. +2. `@opticdev/optic`, this package is a dependency of `@backstage/repo-tools` but is only required for OpenAPI related commands. + +You should install both of the above packages in the _root_ of your workspace. + +## Storing your OpenAPI specification + +You should create a new folder, `src/schema` in your backend plugin to store your OpenAPI (and any other) specifications. For example, if you're adding a specification to the catalog plugin, you would add a `src/schema` folder to `plugins/catalog-backend`, making a `plugins/catalog-backend/src/schema` directory. This directory should have an `openapi.yaml` file inside. + +> Currently, only the `.yaml` extension is supported, not `.yml`. + +## Generating a typed express router from a spec + +Run `yarn backstage-repo-tools schema openapi generate `. This will create an `openapi.generated.ts` file in the `src/schema` directory that contains the OpenAPI schema as well as a generated express router with types. + +Use it like so, update your `router.ts` or `createRouter.ts` file with the following content, + +```diff ++ import { createOpenApiRouter } from '../schema/openapi.generated'; +- import Router from 'express-promise-router'; + +... +export async function createRouter( + options: RouterOptions, +): Promise { ++ const router = await createOpenApiRouter(); +- const router = Router(); +``` + +## Generating a typed client from a spec + +Run `yarn backstage-repo-tools schema openapi generate-client --input-spec /src/schema/openapi.yaml --output-directory `. `` should match the same backend plugin we've been using so far. `` is a new directory and npm package that you should create. The general pattern is `plugins/-client`. + +The generated client will have a directory `src/generated` that exports a `DefaultApiClient` class and all generated types. You can use the client like so, + +```diff ++ import { DefaultApiClient } from './generated'; + +export class CatalogClient implements CatalogApi { ++ private readonly apiClient: DefaultApiClient; + + constructor(options: { + discoveryApi: { getBaseUrl(pluginId: string): Promise }; + fetchApi?: { fetch: typeof fetch }; + }) { ++ this.apiClient = new DefaultApiClient(options); + } + ... +``` + +usage of the types will depend on your type names. + +You should be able to use the generated `DefaultApi.client.ts` file out of the box for your API needs. For full customization, you can use a wrapper around the generated client to adjust the flavor of your clients. + +For more information, see [the docs](./generate-client.md). + +## Validating your spec with test traffic + +Add the following lines to your `createRouter.test.ts` or `router.test.ts` file, + +```diff ++ import { wrapInOpenApiTestServer } from '@backstage/backend-openapi-utils'; ++ import { Server } from 'http'; + +... + +describe('createRouter', () => { +- let app: express.Express; ++ let app: express.Express | Server; + +... + +- app = express().use(router); ++ app = wrapInOpenApiTestServer(express().use(router)); +``` + +This adds a wrapper around the express server that allows it to reroute traffic for `supertest`. Run `yarn backstage-repo-tools schema openapi init` to create some required config files. Now, when you run `yarn backstage-repo-tools schema openapi test` your schema will now be tested against your test data. Any errors will be reported. + +Our command is a small wrapper over [`Optic`](https://github.com/opticdev/optic) which does all of the heavy lifting. + +For more information, see [the docs](./test-case-validation.md). diff --git a/docs/openapi/generate-client.md b/docs/openapi/generate-client.md index 10e6ec3b2f..4afa7d64d7 100644 --- a/docs/openapi/generate-client.md +++ b/docs/openapi/generate-client.md @@ -1,3 +1,9 @@ +--- +id: generate-client +title: Generate a client from your OpenAPI spec +description: Documentation on how to create a client for a given OpenAPI spec +--- + ## How to generate a client with `repo-tools schema openapi generate-client`? ### Prerequisites diff --git a/docs/openapi/test-case-validation.md b/docs/openapi/test-case-validation.md index 50686b5ba0..0d93b6d5b5 100644 --- a/docs/openapi/test-case-validation.md +++ b/docs/openapi/test-case-validation.md @@ -1,3 +1,9 @@ +--- +id: test-case-validation +title: Validate your OpenAPI spec against test data +description: Documentation on how to use the `schema openapi test` command. +--- + ## OpenAPI Validation using Test Cases This is primarily performed by `backstage-repo-tools schema openapi test`. Any errors found in the generated specs can be either diff --git a/microsite/sidebars.json b/microsite/sidebars.json index d325487a7c..5dd5ce93f8 100644 --- a/microsite/sidebars.json +++ b/microsite/sidebars.json @@ -249,6 +249,15 @@ "plugins/internationalization", "plugins/analytics", "plugins/feature-flags", + { + "type": "category", + "label": "OpenAPI", + "items": [ + "openapi/01-getting-started", + "openapi/generate-client", + "openapi/test-case-validation" + ] + }, { "type": "category", "label": "Backends and APIs", diff --git a/mkdocs.yml b/mkdocs.yml index 4642ab9ba3..bf4024759b 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -128,6 +128,10 @@ nav: - Composability System: 'plugins/composability.md' - Plugin Analytics: 'plugins/analytics.md' - Feature Flags: 'plugins/feature-flags.md' + - OpenAPI: + - Schema-first plugins with OpenAPI (Experimental): 'openapi/01-getting-started.md' + - Generate a client from your OpenAPI spec: 'openapi/generate-client.md' + - Validate your OpenAPI spec against test data: 'openapi/test-case-validation.md' - Backends and APIs: - Proxying: 'plugins/proxying.md' - Backend plugin: 'plugins/backend-plugin.md'