@hey-api/json-schema-ref-parser
@hey-api/json-schema-ref-parser is Hey API's ESM-only fork of the familiar JSON Schema reference parser. Version 1.4.4 can parse one JSON or YAML schema from an object, local path, URL, or supplied ArrayBuffer; bundle external references into internal pointers; and merge several OpenAPI-shaped inputs with collision prefixes through bundleMany. It exposes a reference map and structured errors. Despite the package name and README, the published class does not expose public resolve or dereference methods.
Install this fork when Hey API integration or its opinionated OpenAPI bundleMany merge is the requirement. For a general reference parser, the missing public dereference and resolver controls, ESM-only Node 22 floor, and unsafe defaults around untrusted external refs make the upstream package a better fit.
Use it if
- You already use Hey API's Node 22 toolchain and need the exact bundling behavior consumed by that ecosystem
- You want to combine multi-file OpenAPI inputs while keeping internal $ref pointers instead of expanding every reference
- You need bundleMany's opinionated prefixing of components, operation ids, conflicting paths, and duplicate tags
- All schemas and external reference targets are trusted or isolated from sensitive networks and files
- You need dereference or resolve: 1.4.4's emitted $RefParser class has parse, bundle, bundleMany, and mergeMany, but no public dereference or resolve method even though the README advertises both and includes a parser.dereference example
- You need a drop-in replacement for @apidevtools/json-schema-ref-parser: method signatures use one options object, static convenience methods are absent, only ESM import is exported, and the minimum Node version is 22.18.0
- Schemas are untrusted on a server: bundle automatically follows arbitrary external file and HTTP references, while the public options expose no resolver allowlist or switch to disable external resolution, creating local-file-read and server-side request risk
- You need browser support: the README inherits a browser-compatibility claim, but the published package declares Node >=22.18.0, imports node:fs behavior through its file resolver, uses Buffer, and exposes only a Node-oriented ESM build
- You need JSON Schema or OpenAPI validation: parsing and reference bundling do not enforce dialect keywords, required OpenAPI structure, operation correctness, or schema semantics
Setup reality
Install @hey-api/json-schema-ref-parser only on Node 22.18.0 or newer. Version 1.4.4 is ESM-only, so use import { $RefParser } and do not call require(); the export map contains only an import target and package.json. TypeScript declarations are included. Runtime dependencies are js-yaml, @jsdevtools/ono, and @types/json-schema. Every public operation takes one object argument: parse returns { schema }, while bundle and bundleMany return a schema directly. parse reads only the root input and leaves $ref strings alone. bundle reads the root, recursively opens every external reference, and rewrites the graph to internal pointers. Local paths use fs.promises.readFile; URLs use global fetch with a 60-second per-request abort and no response-size cap. The fetch RequestInit passed to bundle is used for the root URL, but nested external resolution calls the URL resolver without carrying those headers or options. There is no public resolver registry, host allowlist, offline flag, or external:false option in this fork. If input is not fully trusted, use parse only, reject external references yourself, or run bundling in a process with restricted filesystem and network access. Circular structures remain references after bundle and can be serialized. The README's dereference options, circular object behavior, equality claim, and hook example do not match a callable 1.4.4 method. bundleMany is not a neutral JSON Schema concatenator: source shows OpenAPI and Swagger-specific merging of info, servers, paths, tags, operationId, and components, with filenames used as prefixes when names collide. Raw object input with $id is described as setting a reference base, but 1.4.4 reduces that id to its origin, so test relative references under nested URL paths. This library resolves pointers; it does not validate the resulting schema or guarantee that merged APIs keep your intended routing semantics.
Patterns
Parse a local JSON or YAML fileparse-local-schema
import { $RefParser } from '@hey-api/json-schema-ref-parser';
const parser = new $RefParser();
const { schema } = await parser.parse({
pathOrUrlOrSchema: './schemas/root.yaml',
});
console.log(schema);parse reads only the root document and returns an object containing schema. It does not resolve, bundle, dereference, or validate $ref targets.
Parse an in-memory schema objectparse-schema-object
const input = {
$schema: 'https://json-schema.org/draft/2020-12/schema',
type: 'object',
properties: { id: { type: 'string' } },
};
const { schema } = await new $RefParser().parse({
pathOrUrlOrSchema: input,
});Without $id, the raw object is used directly and no I/O occurs. Parsing checks that the root is object-like, not that it conforms to a JSON Schema dialect.
Parse a remote root with request headersparse-remote-root
const parser = new $RefParser();
const { schema } = await parser.parse({
pathOrUrlOrSchema: 'https://api.example.com/private/openapi.yaml',
fetch: {
headers: { Authorization: `Bearer ${token}` },
},
});The RequestInit applies to this root request. If you switch to bundle, nested remote references are fetched without inheriting these headers in version 1.4.4.
Bundle a multi-file schemabundle-external-references
const parser = new $RefParser();
const bundled = await parser.bundle({
pathOrUrlOrSchema: './schemas/root.yaml',
});
await fs.writeFile(
'dist/schema.json',
JSON.stringify(bundled, null, 2)
);bundle recursively reads external file and URL references, then rewrites them as internal pointers. Use only trusted inputs or a filesystem and network sandbox.
Bundle from already fetched root bytesbundle-supplied-bytes
const response = await fetch(rootUrl, {
headers: { Authorization: `Bearer ${token}` },
});
if (!response.ok) throw new Error(`HTTP ${response.status}`);
const bundled = await new $RefParser().bundle({
pathOrUrlOrSchema: rootUrl,
arrayBuffer: await response.arrayBuffer(),
});The URL still establishes the base for relative references. Only the root bytes are supplied; nested references can still trigger unauthenticated network requests.
List every source read during bundlinginspect-resolved-sources
const parser = new $RefParser();
await parser.bundle({ pathOrUrlOrSchema: './schemas/root.yaml' });
console.log(parser.$refs.paths());
console.log(parser.$refs.paths('file'));
console.log(parser.$refs.paths('http'));Inspecting paths after the operation is useful for audits and reproducible builds, but it does not prevent an unwanted read that already occurred.
Read the parser's reference mapinspect-resolved-values
const parser = new $RefParser();
await parser.bundle({ pathOrUrlOrSchema: './schemas/root.yaml' });
const files = parser.$refs.values('file');
for (const [source, value] of Object.entries(files)) {
console.log(source, typeof value);
}$refs contains whole-document values keyed by normalized source URLs. Treat it as operation state; creating a new parse on the same parser resets the map.
Keep recursive schemas serializablebundle-circular-schema
const parser = new $RefParser();
const bundled = await parser.bundle({
pathOrUrlOrSchema: './schemas/recursive.yaml',
});
const json = JSON.stringify(bundled);Bundling retains internal $ref pointers, so recursive schemas do not become circular JavaScript object graphs and JSON.stringify remains usable.
Merge and bundle several OpenAPI inputsmerge-openapi-inputs
const parser = new $RefParser();
const merged = await parser.bundleMany({
pathOrUrlOrSchemas: [
'./specs/catalog.yaml',
'./specs/billing.yaml',
{ openapi: '3.1.0', info: { title: 'Inline', version: '1' }, paths: {} },
],
});bundleMany uses OpenAPI and Swagger-specific merge rules. Components and operation ids gain filename prefixes, and conflicting path methods can move under a prefixed route. Review the resulting API contract.
Restrict parsing to JSON inputsdisable-yaml-parser
const parser = new $RefParser();
parser.options.parse.yaml.canHandle = () => false;
parser.options.parse.text.canHandle = () => false;
parser.options.parse.binary.canHandle = () => false;
const bundled = await parser.bundle({
pathOrUrlOrSchema: './schemas/root.json',
});Parser plugins control syntax recognition, not which files or hosts may be read. External JSON references remain enabled and need separate trust controls.
Inspect grouped reference errorshandle-parser-errors
import {
JSONParserErrorGroup,
isHandledError,
} from '@hey-api/json-schema-ref-parser';
try {
await parser.bundle({ pathOrUrlOrSchema: input });
} catch (error) {
if (error instanceof JSONParserErrorGroup) {
for (const item of error.errors) {
console.error(item.code, item.source, item.message);
}
} else if (isHandledError(error)) {
console.error(error.code, error.source, error.message);
} else {
throw error;
}
}Root read and parse failures can surface directly, while external resolution and bundling can aggregate handled errors. Do not rely on message text alone when code is available.
Reject external references before trusted processingreject-external-references
function assertInternalRefs(value) {
if (!value || typeof value !== 'object') return;
if ('$ref' in value && typeof value.$ref === 'string' && !value.$ref.startsWith('#')) {
throw new Error(`External $ref rejected: ${value.$ref}`);
}
for (const child of Object.values(value)) assertInternalRefs(child);
}
const { schema } = await parser.parse({ pathOrUrlOrSchema: upload });
assertInternalRefs(schema);Version 1.4.4 has no public external:false or resolver allowlist. Parse first and reject external refs before calling bundle on user-controlled schemas.
Alternatives
| Package | Registry | Pick it when |
|---|---|---|
| @apidevtools/json-schema-ref-parser | npm | Use the upstream package when you need documented parse, resolve, bundle, and dereference APIs with configurable resolvers |
| @apidevtools/swagger-parser | npm | Use OpenAPI and Swagger parsing plus schema and specification validation instead of generic reference bundling alone |
| @stoplight/json-ref-resolver | npm | Use a resolver designed around custom authority handlers when control over remote lookup is central |
| json-refs | npm | Use lower-level JSON Reference discovery, resolution, and pointer utilities for an established CommonJS workflow |