Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
dee85ef
Phase 1: add filter config schema, types & minimatch dependency
jonaslagoni Jul 23, 2026
6281eec
Phase 2: failing tests for matchesFilter glob utility (TDD RED)
jonaslagoni Jul 23, 2026
9ff4756
Phase 3: implement matchesFilter glob utility (TDD GREEN)
jonaslagoni Jul 23, 2026
690d193
Phase 4: expected-output-first runtime filter fixtures & specs
jonaslagoni Jul 23, 2026
b1144fe
Phase 5: failing tests for AsyncAPI filter helper v2+v3 (TDD RED)
jonaslagoni Jul 23, 2026
22612a2
Phase 6: implement AsyncAPI filter helper + wire into all loaders (TD…
jonaslagoni Jul 23, 2026
fbcfd5f
Phase 7: failing tests for OpenAPI filter + orphan pruning v2+v3 (TDD…
jonaslagoni Jul 23, 2026
3c29d16
Phase 8: implement OpenAPI filter + orphan-prune walker, wire into lo…
jonaslagoni Jul 23, 2026
545ebb8
Phase 9: config-validation tests for filter; verify threading across …
jonaslagoni Jul 23, 2026
60e77ba
Phase 10: add openapi-filtering example
jonaslagoni Jul 23, 2026
a9c2887
Phase 11: document root-config filter option
jonaslagoni Jul 23, 2026
3fe110a
Phase 13: regenerate schemas + doc ToCs for filter config
jonaslagoni Jul 23, 2026
9591bc8
Phase 14: runtime verification — commit filtered runtime expected output
jonaslagoni Jul 23, 2026
d67d3e0
Phase 15: revert non-idempotent ToC + platform-string noise from gene…
jonaslagoni Jul 23, 2026
413a5da
Phase 16: extract shared collectExtensionValues walker; plan complete
jonaslagoni Jul 23, 2026
5bb276c
fix
jonaslagoni Jul 23, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 60 additions & 0 deletions docs/configurations.md
Original file line number Diff line number Diff line change
Expand Up @@ -210,3 +210,63 @@ paths:
remote URL, the input watcher is skipped and a warning is logged. Use
`--watchPath` to watch a local file that triggers regeneration (which
will re-fetch the URL) on change.

## Filtering channels, operations & paths

The optional root-level `filter` field restricts code generation to a subset of
the input document instead of everything. It is available on the **AsyncAPI**
and **OpenAPI** input branches; JSON Schema input has no `filter` (there are no
channels/paths to filter).

```javascript
export default {
inputType: 'openapi',
inputPath: './openapi.yaml',
filter: {
include: ['/users', '/users/**', '/orders'],
exclude: ['/users/{id}/audit']
},
generators: [
{ preset: 'payloads', outputPath: './src/payloads' }
]
};
```

### Semantics

- Patterns are [minimatch](https://github.com/isaacs/minimatch) globs.
- **`include`** — an item is kept when it matches any include pattern. An empty
(or absent) `include` includes everything.
- **`exclude`** — applied *after* `include`; an item matching any exclude
pattern is always dropped. An empty `exclude` excludes nothing.
- With **no** `filter` (or empty `include` + `exclude`) the output is identical
to generating without the field — the feature is opt-in and default-off.

### What patterns match against

| Input type | An item matches when a pattern matches any of… |
| ---------- | ---------------------------------------------- |
| AsyncAPI | the channel **address**, the channel **id**, or the **operation id** |
| OpenAPI | the **path template** (e.g. `/users/{id}`) or the **operationId** (the spec's `operationId`, or a derived id when absent) |

Matching an operation retains its parent channel/path; matching a channel/path
directly retains it even if none of its operations match.

> Note: `/users/**` matches nested paths like `/users/{id}/audit` but **not**
> `/users` itself — list both when you want the collection *and* everything
> under it.

### Orphan pruning

When a filter is active, component schemas (and, for AsyncAPI, messages) left
unreferenced by the retained channels/operations/paths are pruned automatically,
so the generated output contains no models for filtered-out surfaces. A schema
still referenced by a retained surface — including via nested references — is
kept. Pruning only runs when a filter is active; the no-filter path never prunes.

Because filtering happens once while the document is loaded, **every** generator
(payloads, parameters, headers, types, channels, client, and all protocols) sees
the already-subsetted document — no per-generator configuration is required.

See the [`openapi-filtering` example](https://github.com/the-codegen-project/cli/tree/main/examples/openapi-filtering)
for a runnable walkthrough.
24 changes: 24 additions & 0 deletions docs/inputs/asyncapi.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,30 @@ There is a lot of overlap with existing tooling, however the idea is to form the
via the `auth` field. See the [configurations guide](../configurations.md#remote-url-inputs) for examples and the [auth scope and security
considerations](../configurations.md#auth-scope-and-security-considerations) section before using `auth` against a public spec — the configured headers are sent to every `$ref` target as well as the root URL.

## Filtering channels & operations

Use the root-level `filter` field to generate code for only a subset of the
document's channels/operations. Glob patterns are matched against the channel
**address**, the channel **id**, or the **operation id**:

```javascript
export default {
inputType: 'asyncapi',
inputPath: './asyncapi.yaml',
filter: {
include: ['user/**', 'orders/created'],
exclude: ['**/internal']
},
generators: [ /* ... */ ]
};
```

`exclude` is applied after `include`; component messages/schemas left orphaned by
the filtering are pruned automatically. Works for both AsyncAPI v2 and v3. With
no `filter`, output is unchanged. See the
[filtering section of the configurations guide](../configurations.md#filtering-channels-operations--paths)
for full semantics.

## Basic AsyncAPI Document Structure

Here's a complete basic AsyncAPI document example to get you started:
Expand Down
28 changes: 28 additions & 0 deletions docs/inputs/openapi.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,34 @@ Create a configuration file that specifies OpenAPI as the input type:

`inputPath` accepts an `http://` or `https://` URL. Optional authentication (bearer token, API key, or custom headers) is configured via the `auth` field. Cross-spec `$ref` URLs are also resolved through the same auth-aware HTTP client. See the [configurations guide](../configurations.md#remote-url-inputs) for examples and the [auth scope and security considerations](../configurations.md#auth-scope-and-security-considerations) section — the configured headers are sent to every `$ref` target as well as the root URL.

## Filtering paths & operations

Use the root-level `filter` field to generate code for only a subset of the
document's paths/operations. Glob patterns are matched against the **path
template** or the **operationId** (the spec's `operationId`, or a derived id when
absent):

```javascript
export default {
inputType: 'openapi',
inputPath: './openapi.yaml',
filter: {
include: ['/users', '/users/**', '/orders'],
exclude: ['/users/{id}/audit']
},
generators: [ /* ... */ ]
};
```

`exclude` is applied after `include`; component schemas (`components.schemas` for
3.x, `definitions` for 2.0) left orphaned by the filtering are pruned
automatically. Only real HTTP methods on a path item are treated as operations —
`parameters`, `servers`, `summary`, and `description` are preserved on retained
paths. With no `filter`, output is unchanged. See the
[filtering section of the configurations guide](../configurations.md#filtering-channels-operations--paths)
for full semantics, and the
[`openapi-filtering` example](https://github.com/the-codegen-project/cli/tree/main/examples/openapi-filtering).

## Troubleshooting

## FAQ
Expand Down
3 changes: 3 additions & 0 deletions examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ A comprehensive example showing how to generate TypeScript types from AsyncAPI s
### [OpenAPI HTTP Client](./openapi-http-client/)
A minimal, self-contained example of generating a type-safe HTTP client from an OpenAPI document — and how to consume it: building request bodies, supplying path parameters, and reading typed responses.

### [OpenAPI Filtering](./openapi-filtering/)
A minimal example of the root-config `filter` option: generate code for only a subset of an OpenAPI document's paths/operations, with orphaned component schemas pruned automatically.

## Getting Started

1. Choose an example that matches your use case
Expand Down
73 changes: 73 additions & 0 deletions examples/openapi-filtering/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
# OpenAPI Filtering

A minimal, self-contained example of the root-config **`filter`** option, which
restricts code generation to a subset of the input document's paths/operations
instead of everything.

## The API

[`openapi.json`](./openapi.json) defines four operations:

| Path | Operation | Response schema |
| ---------------------- | -------------- | --------------- |
| `GET /users` | `listUsers` | `User` (→ `Address`) |
| `GET /users/{id}/audit`| `getUserAudit` | `AuditEntry` |
| `GET /orders` | `listOrders` | `Order` |
| `GET /metrics` | `getMetrics` | `Metrics` |

## The filter

[`codegen.config.js`](./codegen.config.js):

```js
filter: {
include: ['/users', '/users/**', '/orders'],
exclude: ['/users/{id}/audit']
}
```

Patterns are [minimatch](https://github.com/isaacs/minimatch) globs, matched
against the **path template** or the **operationId**. `exclude` is applied after
`include`, so an excluded item is always dropped. An empty/absent `filter`
generates everything, unchanged.

> Note: `/users/**` matches nested paths like `/users/{id}/audit` but **not**
> `/users` itself — list both when you want the collection and everything under
> it.

## What gets generated

Running the generator:

```bash
npm run generate
```

produces payload models for **only the retained operations**:

```
src/generated/payloads/
├── User.ts # kept: /users
├── Address.ts # kept: referenced by User (nested)
├── ListUsersResponse_200.ts # kept: /users response
├── Order.ts # kept: /orders
└── ListOrdersResponse_200.ts # kept: /orders response
```

Filtered out:

- **`/users/{id}/audit`** — matched `include` via `/users/**` but removed by
`exclude`, so no `getUserAudit` models are generated.
- **`/metrics`** — never matched `include`, so it is dropped.
- **`AuditEntry`** and **`Metrics`** — component schemas referenced only by the
dropped operations, so they are **pruned automatically** (orphan pruning).
`Address` survives because it is still referenced by the retained `User`.

## Notes

- Filtering happens once, while the document is loaded, so **every** generator
(payloads, parameters, headers, types, channels, client) sees the already
subsetted document — no per-generator configuration needed.
- The same `filter` option works for AsyncAPI input, where patterns match
against channel address, channel id, or operation id.
- JSON Schema input has no `filter` (it has no paths/channels to filter).
21 changes: 21 additions & 0 deletions examples/openapi-filtering/codegen.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
export default {
inputType: 'openapi',
inputPath: './openapi.json',
// Generate code for only a subset of the API:
// - include: keep everything under /users and the /orders path
// - exclude: drop the internal audit endpoint even though /users/** matched it
// Anything not included (e.g. /metrics) is left out, and component schemas
// that become orphaned (AuditEntry, Metrics) are pruned automatically.
filter: {
include: ['/users', '/users/**', '/orders'],
exclude: ['/users/{id}/audit']
},
generators: [
{
preset: 'payloads',
outputPath: './src/generated/payloads',
language: 'typescript',
serializationType: 'json'
}
]
};
125 changes: 125 additions & 0 deletions examples/openapi-filtering/openapi.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
{
"openapi": "3.0.0",
"info": {
"title": "Store API",
"version": "1.0.0",
"description": "A small API used to demonstrate root-config filtering."
},
"paths": {
"/users": {
"get": {
"operationId": "listUsers",
"summary": "List users",
"responses": {
"200": {
"description": "A list of users",
"content": {
"application/json": {
"schema": {
"type": "array",
"items": {"$ref": "#/components/schemas/User"}
}
}
}
}
}
}
},
"/users/{id}/audit": {
"get": {
"operationId": "getUserAudit",
"summary": "Internal audit log for a user",
"parameters": [
{"name": "id", "in": "path", "required": true, "schema": {"type": "string"}}
],
"responses": {
"200": {
"description": "Audit entries",
"content": {
"application/json": {
"schema": {
"type": "array",
"items": {"$ref": "#/components/schemas/AuditEntry"}
}
}
}
}
}
}
},
"/orders": {
"get": {
"operationId": "listOrders",
"summary": "List orders",
"responses": {
"200": {
"description": "A list of orders",
"content": {
"application/json": {
"schema": {
"type": "array",
"items": {"$ref": "#/components/schemas/Order"}
}
}
}
}
}
}
},
"/metrics": {
"get": {
"operationId": "getMetrics",
"summary": "Operational metrics (not part of the public SDK)",
"responses": {
"200": {
"description": "Metrics",
"content": {
"application/json": {
"schema": {"$ref": "#/components/schemas/Metrics"}
}
}
}
}
}
}
},
"components": {
"schemas": {
"User": {
"type": "object",
"properties": {
"id": {"type": "string"},
"displayName": {"type": "string"},
"address": {"$ref": "#/components/schemas/Address"}
}
},
"Address": {
"type": "object",
"properties": {
"street": {"type": "string"},
"city": {"type": "string"}
}
},
"AuditEntry": {
"type": "object",
"properties": {
"action": {"type": "string"},
"at": {"type": "string", "format": "date-time"}
}
},
"Order": {
"type": "object",
"properties": {
"id": {"type": "string"},
"total": {"type": "number"}
}
},
"Metrics": {
"type": "object",
"properties": {
"uptimeSeconds": {"type": "number"}
}
}
}
}
}
19 changes: 19 additions & 0 deletions examples/openapi-filtering/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"name": "openapi-filtering",
"version": "1.0.0",
"description": "Example showing the root-config `filter` option: generate code for only a subset of an OpenAPI document's paths/operations",
"type": "module",
"scripts": {
"generate": "node ../../bin/run.mjs generate codegen.config.js"
},
"keywords": [
"openapi",
"codegen",
"filter",
"filtering",
"subset"
],
"engines": {
"node": ">=18.0.0"
}
}
Loading
Loading