UNPKG

directus-extension-api-docs

Version:

directus extension for swagger interface and openapi including custom endpoints definitions // custom endpoint validations middleware based on openapi or zod

291 lines (224 loc) 10.8 kB
# directus-extension-api-docs [![npm version](https://img.shields.io/npm/v/directus-extension-api-docs.svg)](https://www.npmjs.com/package/directus-extension-api-docs) [![npm downloads](https://img.shields.io/npm/dm/directus-extension-api-docs.svg)](https://www.npmjs.com/package/directus-extension-api-docs) [![license](https://img.shields.io/npm/l/directus-extension-api-docs.svg)](./LICENSE) > Compatible with Directus `^9 || ^10 || ^11` and both bundled and non-bundled endpoint extensions. Release notes: see [CHANGELOG.md](./CHANGELOG.md). Directus Extension providing: - a Swagger UI interface (OpenAPI 3.x) - an autogenerated OpenAPI specification file (merged core + your custom endpoints) -- including custom endpoint definitions - optional validation middleware for your custom endpoints (based on merged OpenAPI spec). See details below - optional Zod-first route definitions for type-safe validation + OpenAPI auto-generation (see [Zod-first routes](#zod-first-routes-optional)) ![workspace](assets/swagger.png) ## Contents - [Prerequisites](#prerequisites) - [Installation](#installation) - [Configuration (optional)](#configuration-optional) - [Definitions (optional)](#definitions-optional) — YAML - [Validations (optional)](#validations-optional) — YAML-based runtime validation - [Zod-first routes (optional)](#zod-first-routes-optional) — type-safe alternative - [YAML vs Zod](#yaml-vs-zod) - [Contributing](#contributing) · [Reporting issues](#reporting-issues) · [License](#license) ## Prerequisites You must already have a Directus Node.js project running. Ref: https://github.com/directus/directus ## Installation ``` npm install directus-extension-api-docs ``` - Swagger interface: by default `http://localhost:8055/api-docs` - Openapi documentation: by default `http://localhost:8055/api-docs/oas` ## Configuration (optional) To include your custom endpoints in the documentation, create an `oasconfig.yaml` file directly under the `/extensions` folder (recommended structure). Avoid placing it under `/extensions/endpoints` unless using Legacy mode. Options: - `docsPath` _optional_ interface base path (default 'api-docs'). Resulting URLs: `/<docsPath>` and `/<docsPath>/oas`. - `info` _optional_ openapi server info (default extract from package.json) - `tags` _optional_ openapi custom tags (will be merged with all standard and all customs tags) - `publishedTags` _optional_ if specified, only operations containing at least one of these tags are kept; all other paths and unused tags are removed. - `paths` _optional_ custom path objects keyed by full path (e.g. `/my-custom-path/my-endpoint`). These are merged into Directus core paths. - `components` _optional_ custom components (schemas, securitySchemes, etc.) shallow-merged over core components. - `useAuthentication` _optional_ (default false). When true, `/api-docs` and `/api-docs/oas` stay publicly reachable: without valid auth they list only anonymous/public paths (no custom endpoints); with auth they list only paths permitted to that user under Directus Access Policies and custom endpoints. Example below: ``` docsPath: 'api-docs' useAuthentication: true info: title: my-directus-bo version: 1.5.0 description: my server description tags: - name: MyCustomTag description: MyCustomTag description publishedTags: - MyCustomTag components: schemas: UserId: type: object required: - user_id x-collection: directus_users properties: user_id: description: Unique identifier for the user. example: 63716273-0f29-4648-8a2a-2af2948f6f78 type: string ``` ## Definitions (optional) For each endpoint extension, you can define OpenAPI partials by adding an `oas.yaml` file in the root of that endpoint's folder. ### Non-bundled extensions Place the `oas.yaml` file directly in the extension folder: ``` - ./extensions/ ─ oasconfig.yaml (optional) - my-endpoint-extension/ - oas.yaml ``` ### Bundled extensions For bundled extensions, place `oas.yaml` files in each sub-extension's folder under the `src` directory: ``` - ./extensions/ ─ oasconfig.yaml (optional) - my-bundle-extension/ - src/ - routes-endpoint/ - oas.yaml - admin-endpoint/ - oas.yaml ``` This structure follows Directus's standard bundle architecture where each sub-extension (routes, endpoints, hooks, etc.) has its own folder under `src/`. The extension will automatically discover and merge all `oas.yaml` files from these subdirectories. ### Mixed environments Both bundled and non-bundled extensions can coexist in the same project. The extension will automatically detect and merge all `oas.yaml` files from both types. Properties: - `tags` _optional_ openapi custom tags - `paths` _optional_ openapi custom paths - `components` _optional_ openapi custom components Example below (`./extensions/my-endpoint-extensions/oas.yaml`): ``` tags: - name: MyCustomTag2 description: MyCustomTag description2 paths: "/my-custom-path/my-endpoint": post: security: - Auth: [ ] summary: Validate email description: Validate email tags: - MyCustomTag2 - MyCustomTag requestBody: content: application/json: schema: "$ref": "#/components/schemas/UserId" responses: '200': description: Successful request content: application/json: schema: "$ref": "#/components/schemas/Users" '401': description: Unauthorized content: {} '422': description: Unprocessable Entity content: {} '500': description: Server Error content: {} components: schemas: Users: type: object # ref to standard components declaring it empty securitySchemes: Auth: in: header name: Authorization type: apiKey ``` ### Legacy mode Configuration and definitions can also be managed in this legacy structure (still supported, but prefer the simplified root placement): ``` - ./extensions/ - endpoints/ - oasconfig.yaml - my-endpoint-extensions/ - oas.yaml - my-endpoint-extensions2/ - oas.yaml ``` ## Validations (optional) You can enable a request validation middleware based on your merged custom definitions. Call the `validate` function inside your custom endpoint source (`./extensions/my-endpoint-extensions/src/index.js`). Arguments: `validate(router, services, schema, paths?)`. `paths` (optional array) lets you restrict validation to only specific path keys from `oasconfig.yaml` instead of all custom paths. Example below: ``` const { validate } = require('directus-extension-api-docs') export default { id: 'my-custom-path', handler: async (router, { services, getSchema }) => { const schema = await getSchema(); await validate(router, services, schema); // Enable validator router.post('/my-endpoint', async (req, res, next) => { ... }) }, } ``` ## Zod-first routes (optional) An alternative ergonomic API: declare schemas and handlers together. The OpenAPI fragment is generated and merged into the same spec served at `/api-docs/oas`, and request validation runs automatically before each handler. Everything you need ships from this package — no need to import `defineEndpoint` from the Directus SDK. ```ts const { defineEndpoint, z, registerSchema } = require('directus-extension-api-docs'); const UserId = registerSchema( 'UserId', z.object({ user_id: z.string().uuid().openapi({ example: '63716273-0f29-4648-8a2a-2af2948f6f78' }), }), ); module.exports = defineEndpoint('my-custom-path', (route, { services, getSchema }) => { route({ method: 'post', path: '/my-endpoint', tags: ['MyCustomTag'], summary: 'Validate user id', security: [{ Auth: [] }], request: { body: UserId }, responses: { 200: { description: 'OK', schema: UserId }, 401: { description: 'Unauthorized' }, }, handler: async (req, res) => { // req.body is typed: { user_id: string } res.json({ user_id: req.body.user_id }); }, }); }); ``` Notes: - The OpenAPI prefix defaults to `/<id>` so paths in `/api-docs/oas` match the URLs clients actually call (Directus mounts each endpoint extension under `/<id>`). - `services`, `getSchema`, `logger`, ... are available in the setup closure and naturally accessible from each handler. - For finer control (e.g. mixing Zod and raw Express routes), the lower-level `defineRoute(router, config)` is still exported. Public exports (named exports of the package main, alongside `validate`): | Export | Purpose | | ----------------- | -------------------------------------------------------------------------------- | | `defineEndpoint` | Declarative wrapper for a Directus endpoint extension built on Zod routes. | | `defineRoute` | Lower-level route registration when you already have your own router. | | `registerSchema` | Register a reusable Zod schema as `components.schemas.<name>` (emits `$ref`). | | `z` | Re-exported `zod` already extended with `.openapi()` metadata. | | `zodValidator` | The per-slot validation middleware used internally, for advanced use. | Validation errors are returned as HTTP `400` with the same `{ message, errors[] }` envelope used by `express-openapi-validator`, so existing API consumers don't need to change. Coexists with YAML definitions: pick whichever fits each endpoint. ## YAML vs Zod Both pipelines feed the same `/api-docs/oas` document and can be mixed per endpoint. - **YAML** (`oasconfig.yaml` + `oas.yaml`): preferred when the OpenAPI spec is the source of truth, or when you import an existing spec. Runtime validation is opt-in via `validate(...)`. - **Zod**: preferred when you want one source of truth that also types `req.body` / `req.params` / `req.query` and validates by default. No separate YAML file to keep in sync. ## Contributing Issues and pull requests are welcome. See [CONTRIBUTING.md](./CONTRIBUTING.md) for setup, scripts, and commit conventions. ## Reporting issues Bugs and feature requests: [github.com/sacconazzo/directus-extension-api-docs/issues](https://github.com/sacconazzo/directus-extension-api-docs/issues). For security vulnerabilities, please follow [SECURITY.md](./SECURITY.md) instead of opening a public issue. ## License [MIT](./LICENSE) © Giona Righini (sacconazzo)