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
Markdown
# directus-extension-api-docs
[](https://www.npmjs.com/package/directus-extension-api-docs)
[](https://www.npmjs.com/package/directus-extension-api-docs)
[](./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))

## 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)