oas-schema-walker
oas-schema-walker is a tiny CommonJS helper that visits an OpenAPI 3 schema object and calls your callback for the root and recognized nested schemas. It descends through items, properties, patternProperties, additional properties or items, allOf, anyOf, oneOf, and not while tracking depth and repeated object references. It does not parse an OpenAPI document, resolve $ref targets, validate schemas, or walk paths and operations; you must first select the Schema Object you want to inspect.
This is a compact fit for legacy oas-kit code that walks known OpenAPI 3.0 schema shapes. For new tooling, its missing 3.1 keywords, absent ref resolution, stale release, and undocumented state quirks make a broader parser or JSON Schema walker the safer install.
Use it if
- You already use the Mermade oas-kit packages and need the same schema traversal behavior they expect
- A CommonJS script needs a dependency-free pre-order walk over OpenAPI 3.0 schema composition and property keywords
- You need to inspect or mutate schema nodes in place through a callback and can control the input shape
- Repeated JavaScript object references or cycles are possible and a WeakMap-based stop condition is sufficient
- You need to walk a whole OpenAPI document: the source only traverses one Schema Object and knows nothing about paths, operations, parameters, request bodies, responses, or components outside that schema
- You need OpenAPI 3.1 or general JSON Schema 2020-12 coverage: the walker does not descend through keywords such as prefixItems, contains, if, then, else, dependentSchemas, propertyNames, or unevaluatedProperties
- You need $ref resolution: a ref node is reduced to a temporary object containing $ref and optionally description, its target is never loaded, and sibling keywords are not traversed
- You expect TypeScript support or a modern module build: version 1.1.5 ships CommonJS only and declares no first-party types
- You require active package releases and tests: npm 1.1.5 dates to July 2020, the package.json test script intentionally exits with an error, and the monorepo's last push was in October 2023
Setup reality
npm install oas-schema-walker adds no runtime dependencies, native build, peer dependency, credential, or config file. Import getDefaultState and walkSchema with require(), then call walkSchema(schema, parent, state, callback). The README's generated signature is confusing, but the source order is schema first, then parent, state, and callback; use {} as the root parent and getDefaultState() for state. Passing an object without depth also triggers default initialization. The callback runs before cycle detection, so a repeated object reference is reported once more when encountered but is not descended again. state is one mutable object shared across the entire traversal: depth is useful, top becomes false after the root and is not restored, and property records the most recently selected child such as properties/name rather than a durable full path. Copy any path information inside the callback instead of retaining state by reference. $ref nodes are special: the callback receives a new minimal object, not the original node, and all siblings are discarded unless allowRefSiblings keeps description. combine can flatten a single-entry allOf, anyOf, or oneOf into the node presented to the callback, but because the implementation reassigns a local variable, do not assume the parent's original child was replaced. For transformations, clone the document first if callers still need the original, and write tests for every schema keyword your documents use.
Patterns
Visit every recognized schema nodewalk-schema
const { getDefaultState, walkSchema } = require('oas-schema-walker');
walkSchema(schema, {}, getDefaultState(), (node, parent, state) => {
console.log(state.depth, node.type);
});The argument order is schema, parent, state, callback. The generated README signature is easy to misread.
Collect nodes reached through propertiescollect-property-nodes
const properties = [];
walkSchema(schema, {}, getDefaultState(), (node, parent, state) => {
if (state.property && state.property.startsWith('properties/')) {
properties.push({ name: state.property.slice(11), node });
}
});state.property is only the latest relative edge, not a complete JSON Pointer, and the same state object is reused. Copy values immediately.
Count visited schema nodescount-schema-nodes
let count = 0;
walkSchema(schema, {}, getDefaultState(), () => {
count += 1;
});
console.log(count);A repeated object reference still triggers the callback on its repeated encounter because the seen check happens after callback.
Collect required names from object schemascollect-required-fields
const required = new Set();
walkSchema(schema, {}, getDefaultState(), (node) => {
for (const name of node.required || []) required.add(name);
});The set merges names from every nesting level. Track your own path if identical property names in different objects must stay separate.
Find deprecated schema nodesfind-deprecated-nodes
const deprecated = [];
walkSchema(schema, {}, getDefaultState(), (node, parent, state) => {
if (node.deprecated === true) {
deprecated.push({ edge: state.property, node });
}
});Store a copied edge or path, not state itself, because later traversal mutates the shared state object.
Mutate ordinary schema nodes in placeadd-missing-descriptions
walkSchema(schema, {}, getDefaultState(), (node) => {
if (!node.description && node.type) {
node.description = `A ${node.type} value`;
}
});Callbacks receive original objects for ordinary nodes, so mutations persist. Clone schema first when mutation must not affect the caller.
Collect reference stringsdetect-ref-nodes
const refs = [];
walkSchema(schema, {}, getDefaultState(), (node) => {
if (node.$ref) refs.push(node.$ref);
});The walker does not resolve refs. The callback receives a new minimal object and traversal stops at that ref.
Preserve descriptions beside refskeep-ref-descriptions
const state = getDefaultState();
state.allowRefSiblings = true;
walkSchema(schema, {}, state, (node) => {
if (node.$ref) console.log(node.$ref, node.description);
});Only description is retained as a ref sibling. Other siblings are dropped from the temporary callback node and are not traversed.
Flatten single-entry composition for inspectioncombine-single-branch
const state = getDefaultState();
state.combine = true;
walkSchema(schema, {}, state, (node) => {
console.log(node.type, node.allOf);
});combine affects single-entry allOf, anyOf, and oneOf. Treat it as a callback view, because the locally replaced object may not replace its parent's child reference.
Inspect only shallow nodeslimit-by-depth
walkSchema(schema, {}, getDefaultState(), (node, parent, state) => {
if (state.depth <= 2) {
console.log(state.depth, state.property);
}
});This filters callback work only; the walker still descends through every recognized child because callbacks cannot prune traversal.
Select schemas from an OpenAPI documentwalk-component-schema
for (const [name, schema] of Object.entries(api.components?.schemas || {})) {
walkSchema(schema, {}, getDefaultState(), (node) => {
console.log(name, node.type);
});
}oas-schema-walker does not find component schemas for you. Select each Schema Object from the OpenAPI document first.
Detect keywords the walker will not descendguard-unsupported-keywords
const unsupported = ['prefixItems', 'contains', 'if', 'then', 'else', 'dependentSchemas'];
walkSchema(schema, {}, getDefaultState(), (node) => {
for (const key of unsupported) {
if (key in node) console.warn(`Manual traversal needed for ${key}`);
}
});OpenAPI 3.1 uses the JSON Schema 2020-12 vocabulary, which is broader than this walker's hard-coded child list.
Alternatives
| Package | Registry | Pick it when |
|---|---|---|
| json-schema-traverse | npm | You need a focused JSON Schema walker with callback paths and broader keyword handling |
| @apidevtools/swagger-parser | npm | You need to parse, validate, bundle, and dereference complete Swagger or OpenAPI documents |
| @apidevtools/json-schema-ref-parser | npm | Resolving and bundling local or remote $ref targets is the central requirement |
| @readme/openapi-parser | npm | You want a maintained parser and validator aimed at complete modern OpenAPI documents |