You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: docs/content/docs/reference/configuration/output.mdx
+118Lines changed: 118 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -1722,6 +1722,124 @@ Make the `options` parameter required. Since the `options` parameter appears las
1722
1722
1723
1723
How to sort properties in generated types.
1724
1724
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.
0 commit comments