swagger-api / swagger-api/apidom
Add $dynamicRef dereference support for OpenAPI 3.1 and 3.2 strategies
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 100
- Forks
- 26
- Avg merge
- 14h 36m
- Merged PRs (30d)
- 22
Description
Background
Issue #378 requested $dynamicRef / $dynamicAnchor dereference support for the OpenAPI 3.1 namespace in 2021. It was closed in November 2025 without a dedicated implementation. Issue #306 is the parent JSON Schema 2020-12 dereferencing epic.
ApiDOM already parses $dynamicRef and $dynamicAnchor into the element tree, but the OpenAPI 3.1 and 3.2 dereference strategies only dereference $ref. Schemas that rely on dynamic references currently pass through unresolved.
Ecosystem context
The openapi-dynamicref-adoption-tracker repository tracks $dynamicRef / $dynamicAnchor support across the OpenAPI tooling ecosystem (SDK generators, parsers, validators). It provides:
- Validator-backed fixtures for recursive trees, nested generics, and non-identifier schema keys
- A compatibility matrix covering 9 SDK generators across the TypeScript ecosystem
- A state-of-the-union snapshot with per-fixture AJV / Hyperjump validation results
- An implementation guide for adding
$dynamicRefsupport to generators and parsers - A combined showcase fixture (
petstore-dynamicref-showcase.yaml) exercising all$dynamicRefpatterns in a realistic API
The initial SDK snapshot confirmed: no tested tool preserves $dynamicRef type fidelity. Generators either fail to parse specs containing $dynamicAnchor, emit unknown/any/Object for dynamic ref slots, or materialize generic/template fixtures as duplicate concrete types. This PR adds the parser-level dereferencing that downstream tools need.
Motivation
Downstream tools that consume ApiDOM dereferenced output should be able to work with OpenAPI 3.1/3.2 schemas that use JSON Schema 2020-12 dynamic references for recursive schemas and generic schema templates.
Proposed stages
Each stage corresponds to a single commit in PR #5177. They can be split into separate PRs if the maintainer prefers smaller reviews.
Stage 1: Add $dynamicAnchor selectors and error types
Closed by 175cccd
- Add
selectors/$dynamicAnchor.tsto bothopenapi-3-1andopenapi-3-2dereference strategiesisDynamicAnchor— validates anchor syntax (^[A-Za-z_][A-Za-z_0-9.-]*$)uriToDynamicAnchor— extracts anchor token from URI fragmentevaluate— finds the schema element with a matching$dynamicAnchorvalue viaapidom-core'sfindparse— validates anchor format, throwsInvalidJsonSchema$dynamicAnchorErroron invalid input
- Add error class hierarchy:
JsonSchema$dynamicAnchorError(base, extendsApiDOMError)EvaluationJsonSchema$dynamicAnchorError(evaluation failure)InvalidJsonSchema$dynamicAnchorError(syntax validation failure)
Stage 2: Add $dynamicRef dereferencing in both visitors
Closed by 1052435
- Add
resolveSchema$dynamicRefFieldtoutil.tsin both strategies — computes the base URI for a$dynamicRefby walkingancestorsSchemaIdentifiers, matching the existingresolveSchema$refFieldpattern - Add
resolveSchema$dynamicRefmethod to bothOpenAPI3_1DereferenceVisitorandOpenAPI3_2DereferenceVisitor- Walks the dynamic scope (ancestor lineage, innermost-first) looking for a schema with a matching
$dynamicAnchor - Falls back to document-level resolution when no ancestor in dynamic scope has a matching anchor — resolves the URI, loads the target resource, searches for the anchor or evaluates the JSON Pointer
- Uses the same merge/transclusion semantics as
$ref: referencing-element properties are merged on top of the transcluded schema,$dynamicRefkeyword is removed from output - Preserves all dereference metadata:
ref-fields($dynamicRef,$dynamicRefBaseURI),ref-origin,ref-referencing-element-id - Handles circular references identically to
$ref: max-depth checks,circular: 'error' | 'replace' | 'ignore' - Supports boolean JSON schemas
- Supports external
$dynamicRef— cross-file resolution withresolve.internal/resolve.externalandskipNestedExternaloptions $reftakes precedence — if a schema has both$refand$dynamicRef, existing$refhandling runs;$dynamicRefonly activates when$refis absent
- Walks the dynamic scope (ancestor lineage, innermost-first) looking for a schema with a matching
- Update
SchemaElementvisitor entry point to dispatch toresolveSchema$dynamicRefwhen the schema has$dynamicRefbut not$ref - Update
getNestedVisitorOptionsto handle$dynamicRefinskipNestedExternallogic
Stage 3: Add test coverage for internal, external, fallback, and recursive $dynamicRef
Closed by f085ce8
- Add test fixtures and assertions for both OAS 3.1 and 3.2:
$dynamicRef-internal:$dynamicRefpoints to a$dynamicAnchorin a sibling schema within the same document — verifies transclusion$dynamicRef-external:$dynamicRefpoints to a$dynamicAnchorin an external file — verifies cross-file resolution (withskipNestedExternal)$dynamicRef-fallback:$dynamicRefhas no ancestor override — verifies fallback to document-level$dynamicAnchorsearch$dynamicRef-recursive:$dynamicRefcreates a self-referential tree — verifies circularity detection (child items are the same element reference as parent)
Stage 4: Add petstore dynamic-ref showcase fixture
Closed by a7efa29
- Add the full petstore dynamic-ref showcase fixture from the openapi-dynamicref-adoption-tracker repo as a test fixture for both OAS 3.1 and 3.2
- Exercises all
$dynamicRefpatterns in a realistic API:- Generic response envelope (
ApiEnvelopeTemplatewith$dynamicAnchor: dataType) - Generic pagination (
PaginatedTemplatewith$dynamicAnchor: itemType) - Concrete pagination bindings (
PaginatedPetItems,PaginatedOwnerItems) - Recursive category tree (
BaseSpeciesCategory/LocalizedSpeciesCategorywith$dynamicAnchor: speciesCategory) - Multi-parameter generic template (
ShelterFolderTemplatewithfolderType+resourceType) - Typed request/response bodies
- Generic response envelope (
Expected outcome
Dereferencing should:
- Resolve
$dynamicRefby walking the dynamic scope (ancestor lineage, innermost-first) for a matching$dynamicAnchor - Fall back to document-level anchor search when no dynamic scope match is found
- Remove the
$dynamicRefkeyword from the output - Transclude the matching schema using the same merge semantics as
$ref - Detect and handle circular dynamic references identically to circular
$ref - Apply to both OpenAPI 3.1 and 3.2 dereference strategies
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start by comparing the existing $ref handling in util.ts and the OpenAPI3_1DereferenceVisitor and OpenAPI3_2DereferenceVisitor, then inspect selectors/$dynamicAnchor.ts in both strategies and the SchemaElement visitor entry point. Done means internal, external, fallback, recursive, boolean-schema, and circular-reference cases pass for both OpenAPI versions, with the referenced test fixtures and metadata behavior covered.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- openapi, typescript
- Domain
- api, backend-api-design
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Quiet
- Clarity
- Clearly specified
- Newbie friendliness
- 35/100