Skip to content

Commit aefa7f0

Browse files
committed
docs: add $dynamicRef configuration reference and limitation docs
1 parent 728f5c9 commit aefa7f0

1 file changed

Lines changed: 118 additions & 0 deletions

File tree

  • docs/content/docs/reference/configuration

docs/content/docs/reference/configuration/output.mdx

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1722,6 +1722,124 @@ Make the `options` parameter required. Since the `options` parameter appears las
17221722

17231723
How to sort properties in generated types.
17241724

1725+
### `$dynamicRef` / `$dynamicAnchor` support
1726+
1727+
Orval automatically resolves JSON Schema 2020-12 `$dynamicRef` / `$dynamicAnchor` keywords in OpenAPI 3.1 specs. No configuration is needed.
1728+
1729+
#### Supported patterns
1730+
1731+
| Pattern | Description |
1732+
|---------|-------------|
1733+
| Generic template emission | Schemas with `$defs` entries that have `$dynamicAnchor` but no `$ref` are emitted as TypeScript generic interfaces (e.g., `interface PaginatedResponse<itemType>`). |
1734+
| Type alias binding | Schemas that `$ref` a generic template and bind `$defs` entries with `$dynamicAnchor` + `$ref` are emitted as type aliases (e.g., `type UserListResponse = PaginatedResponse<User>`). |
1735+
| Self-referential `$dynamicAnchor` | Recursive schemas where `$dynamicRef` resolves to the declaring schema itself (e.g., tree nodes). |
1736+
| `allOf` bound aliases | Schemas that combine a generic template reference with additional properties via `allOf` emit intersection types (e.g., `type X = Template<Args> & { extra }`). |
1737+
1738+
#### Generic template example
1739+
1740+
Define a reusable generic schema with an unbound `$dynamicAnchor` in `$defs`:
1741+
1742+
```yaml title="OpenAPI 3.1 spec"
1743+
components:
1744+
schemas:
1745+
PaginatedResponse:
1746+
$defs:
1747+
itemType:
1748+
$dynamicAnchor: itemType
1749+
not: {}
1750+
type: object
1751+
properties:
1752+
items:
1753+
type: array
1754+
items:
1755+
$dynamicRef: '#itemType'
1756+
total:
1757+
type: integer
1758+
```
1759+
1760+
Then bind it to concrete types:
1761+
1762+
```yaml
1763+
UserListResponse:
1764+
$defs:
1765+
itemType:
1766+
$dynamicAnchor: itemType
1767+
$ref: '#/components/schemas/User'
1768+
$ref: '#/components/schemas/PaginatedResponse'
1769+
1770+
OrderListResponse:
1771+
$defs:
1772+
itemType:
1773+
$dynamicAnchor: itemType
1774+
$ref: '#/components/schemas/Order'
1775+
$ref: '#/components/schemas/PaginatedResponse'
1776+
```
1777+
1778+
Generated TypeScript:
1779+
1780+
```ts
1781+
export interface PaginatedResponse<itemType> {
1782+
items: itemType[];
1783+
total: number;
1784+
}
1785+
1786+
export type UserListResponse = PaginatedResponse<User>;
1787+
export type OrderListResponse = PaginatedResponse<Order>;
1788+
```
1789+
1790+
The generic parameter name (`itemType`) comes from the `$dynamicAnchor` value. The type alias name (`UserListResponse`) comes from the schema key in `components.schemas`. Endpoints that reference a bound alias use the alias name directly (e.g., `Promise<AxiosResponse<UserListResponse>>`).
1791+
1792+
#### Self-referential `$dynamicAnchor` example
1793+
1794+
When a schema declares `$dynamicAnchor` and uses `$dynamicRef` with the same anchor, the type resolves to itself:
1795+
1796+
```yaml title="OpenAPI 3.1 spec"
1797+
components:
1798+
schemas:
1799+
BaseCategory:
1800+
$dynamicAnchor: category
1801+
type: object
1802+
properties:
1803+
id:
1804+
type: string
1805+
children:
1806+
type: array
1807+
items:
1808+
$dynamicRef: '#category'
1809+
1810+
LocalizedCategory:
1811+
$dynamicAnchor: category
1812+
allOf:
1813+
- $ref: '#/components/schemas/BaseCategory'
1814+
- type: object
1815+
properties:
1816+
displayName:
1817+
type: string
1818+
```
1819+
1820+
Generated TypeScript:
1821+
1822+
```ts
1823+
export interface BaseCategory {
1824+
id?: string;
1825+
children?: BaseCategory[];
1826+
}
1827+
1828+
export interface LocalizedCategory {
1829+
id?: string;
1830+
children?: LocalizedCategory[];
1831+
displayName?: string;
1832+
}
1833+
```
1834+
1835+
Each schema's `$dynamicRef: '#category'` resolves to its own type because it declares `$dynamicAnchor: category`.
1836+
1837+
#### Limitations
1838+
1839+
- Each schema in `components.schemas` is generated once with a single dynamic scope. If the same named component is referenced by multiple endpoints that each provide different `$defs` bindings, only one binding applies. The common pattern — putting `$defs` bindings on inline response schemas — works correctly.
1840+
- `$dynamicRef` values targeting external documents (e.g., `other.json#anchor`) fall back to `unknown`.
1841+
- Inline `$defs` entries without `$ref` that have `$dynamicAnchor` are treated as generic type parameters, not concrete bindings.
1842+
17251843
### contentType
17261844

17271845
Filter content types:

0 commit comments

Comments
 (0)