@hey-api/json-schema-ref-parser review
@hey-api/json-schema-ref-parser 1.4.4 is Hey API's Node-only fork for reading JSON or YAML schemas and rewriting external `$ref` targets into one document with internal pointers. It accepts a path, URL, object, or supplied ArrayBuffer. Its extra `bundleMany()` operation merges several OpenAPI-shaped inputs, prefixes colliding components and operation IDs, and rewrites conflicting paths. Version 1.4.4 removes CommonJS files from the package and deletes `$ref` keywords the bundler cannot resolve. The published class exposes `parse()`, `bundle()`, and `bundleMany()`; the README's `dereference()` example does not match the shipped API.
@hey-api/json-schema-ref-parser 1.4.4 installed in 2.3 seconds but produced 2 high audit findings, rejected `require()`, and failed our browser build; it fits trusted Node 22 ESM tooling that specifically needs Hey API's `bundleMany()`. General-purpose or user-facing schema services should prefer the upstream parser with explicit resolver controls.
We installed it
| Install | ✓ · 2.3s | 5 packages on disk · 2 MB |
| Import | ½ | ESM import works · require() fails · ESM package with exports map |
| Browser | n/a | could not be bundled for the browser (Node-only code, most likely) |
| Types | ✓ | TypeScript types bundled |
| Known vulns | 2 | 0 critical · 2 high · 0 moderate · 0 low (npm audit) |
Answers from our run
Does @hey-api/json-schema-ref-parser install cleanly?
Yes. In a fresh container with an empty cache, npm install @hey-api/json-schema-ref-parser finished in 2 seconds, leaving 5 packages and 2 MB on disk. npm audit reported 2 known vulnerabilities.
Can @hey-api/json-schema-ref-parser run in a browser?
Not directly: esbuild could not bundle it for the browser in our run, which normally means it depends on Node built-ins. Use it on the server, or find a browser-targeted alternative.
Does @hey-api/json-schema-ref-parser work with both ESM and CommonJS?
ESM only. import '@hey-api/json-schema-ref-parser' worked, require('@hey-api/json-schema-ref-parser') failed in our run, so CommonJS projects need a dynamic import or a build step.
Does @hey-api/json-schema-ref-parser include TypeScript types?
Yes, type declarations ship inside the package, so no @types install is needed.
@hey-api/json-schema-ref-parser or @apidevtools/json-schema-ref-parser: which should you use?
@apidevtools/json-schema-ref-parser: Use the upstream parser for documented parse, resolve, bundle, and dereference operations plus configurable resolvers. @hey-api/json-schema-ref-parser 1.4.4 installed in 2.3 seconds but produced 2 high audit findings, rejected require(), and failed our browser build; it fits trusted Node 22 ESM tooling that specifically needs Hey API's bundleMany().
When should you not use @hey-api/json-schema-ref-parser?
You need resolve() or dereference(). Version 1.4.4's exported class has neither method, although the package README advertises both behaviors.
Use it if
- A Hey API build needs the exact parser and bundling behavior used by the surrounding monorepo.
- Several trusted OpenAPI files must be combined with `bundleMany()` and reviewed after its collision prefixing.
- External JSON or YAML references should become internal pointers while recursive schemas remain serializable.
- The process runs on Node 22.18.0 or newer and uses ESM throughout.
- You need `resolve()` or `dereference()`. Version 1.4.4's exported class has neither method, although the package README advertises both behaviors.
- A CommonJS consumer must call `require()`. Our Node 22.23.2 check failed because the exports map provides an ESM import target only.
- Schemas come from users or unknown hosts. `bundle()` follows file and HTTP references, and the public options offer no host allowlist, root-directory boundary, or `external: false` switch.
- The output must be schema-validated. Parsing and pointer rewriting do not check JSON Schema dialect rules or whether a merged document is a valid OpenAPI contract.
- A browser bundle is required. Our esbuild browser build failed, the package imports Node file behavior, and its engine declaration starts at Node 22.18.0.
- You want a drop-in replacement for `@apidevtools/json-schema-ref-parser`. This fork changes method signatures, omits upstream convenience methods, and exposes different resolver controls.
Setup reality
We installed @hey-api/json-schema-ref-parser 1.4.4 in a fresh Node 22 Bookworm sandbox in 2.3 seconds. The install left 5 packages and 2 MB on disk. npm audit found 2 known vulnerabilities, both high severity. The package has 3 direct dependencies, 0 peers, and a 496 KB unpacked size. Bundled TypeScript declarations are present. ESM import worked, while require() failed under Node 22.23.2.
The manifest requires Node 22.18.0 or newer and exports only dist/index.mjs plus package.json. There are no credentials or native build tools. Each public method takes one object argument rather than the positional signatures used by the upstream parser. parse() reads the root and returns { schema }; it leaves $ref strings unresolved. bundle() returns the schema itself after loading external targets and replacing them with internal pointers.
A local reference is read through the filesystem, and an HTTP reference uses global fetch. Request options supplied through fetch reach the root request, but version 1.4.4 does not carry those headers into every nested URL resolution path. The API has no resolver allowlist or offline flag. For untrusted input, call parse() first, reject every $ref that does not begin with #, or run bundling in a process that cannot reach sensitive files and networks.
bundleMany() applies OpenAPI-specific merge rules to info, servers, paths, tags, operation IDs, and components. A filename can become a prefix when names collide, so diff the generated contract before publishing it. Version 1.4.4 also removes an unresolvable $ref keyword during bundling; that can leave a sibling object with different meaning instead of a visible broken pointer. Our browser bundle attempt failed, so keep this parser in Node tooling. Inspect the 2 high audit findings in the resolved lockfile before CI adoption.
Patterns
Parse one local JSON or YAML document parse-local-file
import { $RefParser } from '@hey-api/json-schema-ref-parser'
const parser = new $RefParser()
const { schema } = await parser.parse({
pathOrUrlOrSchema: './schemas/root.yaml'
})`parse()` reads only the root document and returns `{ schema }`. It does not follow, bundle, or validate `$ref` targets.
Accept an in-memory schema parse-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
})An object without `$id` causes no root I/O. The parser checks that the root is object-like, not that it follows the declared JSON Schema dialect.
Send headers for a remote root parse-private-url
const { schema } = await new $RefParser().parse({
pathOrUrlOrSchema: 'https://api.example.com/openapi.yaml',
fetch: { headers: { Authorization: `Bearer ${token}` } }
})The RequestInit applies to the root fetch. Nested references reached by `bundle()` do not consistently inherit these headers in 1.4.4.
Convert external references to internal pointers bundle-external-refs
import { writeFile } from 'node:fs/promises'
const parser = new $RefParser()
const bundled = await parser.bundle({
pathOrUrlOrSchema: './schemas/root.yaml'
})
await writeFile('dist/schema.json', JSON.stringify(bundled, null, 2))`bundle()` can read arbitrary file and HTTP targets named by the schema. Restrict the process when the input is not fully trusted.
Reuse already fetched root bytes supply-root-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 remains the base for relative pointers. Supplying root bytes does not prevent nested `$ref` values from causing more network requests.
List sources opened during bundling list-read-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'))`$refs.paths()` is useful for an audit after the run. It cannot block a file or URL before the parser reads it.
Inspect a resolved pointer read-reference-value
const parser = new $RefParser()
await parser.bundle({ pathOrUrlOrSchema: './schemas/root.yaml' })
if (parser.$refs.exists('#/components/schemas/User', parser.options)) {
console.log(parser.$refs.get('#/components/schemas/User'))
}The reference map belongs to the latest operation on that parser instance. A new parse resets `$refs`.
Serialize a recursive schema bundle-recursive-schema
const bundled = await new $RefParser().bundle({
pathOrUrlOrSchema: './schemas/tree.yaml'
})
const json = JSON.stringify(bundled)Bundling preserves internal `$ref` pointers, so recursion does not create a circular JavaScript object graph.
Merge several OpenAPI inputs merge-openapi-files
const merged = await new $RefParser().bundleMany({
pathOrUrlOrSchemas: [
'./specs/catalog.yaml',
'./specs/billing.yaml',
{ openapi: '3.1.0', info: { title: 'Inline', version: '1' }, paths: {} }
]
})`bundleMany()` may prefix components, operation IDs, and conflicting routes from source filenames. Review the merged paths before distribution.
Reject user-controlled external pointers reject-external-refs
function rejectExternalRefs(value) {
if (!value || typeof value !== 'object') return
if (typeof value.$ref === 'string' && !value.$ref.startsWith('#')) {
throw new Error(`External $ref rejected: ${value.$ref}`)
}
for (const child of Object.values(value)) rejectExternalRefs(child)
}
const { schema } = await new $RefParser().parse({ pathOrUrlOrSchema: upload })
rejectExternalRefs(schema)Version 1.4.4 has no public `external: false` option. Inspect after `parse()` and before any call to `bundle()`.
Report grouped parser errors handle-reference-errors
import { $RefParser, JSONParserErrorGroup, isHandledError } from '@hey-api/json-schema-ref-parser'
try {
await new $RefParser().bundle({ pathOrUrlOrSchema: input })
} catch (error) {
if (error instanceof JSONParserErrorGroup) {
for (const item of error.errors) console.error(item.code, item.source)
} else if (isHandledError(error)) {
console.error(error.code, error.source)
} else {
throw error
}
}External resolution can group handled errors. Use the exported code and source fields instead of matching message text.
Limit syntax handling to JSON disable-non-json-parsers
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'
})Disabling syntax parsers does not restrict filesystem paths or remote hosts. JSON references can still trigger external reads.
Alternatives
| Package | Registry | Pick it when |
|---|---|---|
| @apidevtools/json-schema-ref-parser | npm | Use the upstream parser for documented parse, resolve, bundle, and dereference operations plus configurable resolvers. |
| @apidevtools/swagger-parser | npm | Use it when an OpenAPI or Swagger document also needs specification validation. |
| @stoplight/json-ref-resolver | npm | Use it when custom authority handlers and explicit control over reference lookup are central requirements. |
| json-refs | npm | Use it for lower-level JSON Reference discovery and resolution in an established CommonJS application. |
More utils guides
lru-cache · ajv · type-fest · p-limit · find-up · js-yaml · the whole shelf →
How this guide is made: grounded in the library's documentation, release notes, changelog, and issue history, on a fixed rubric — not a hands-on install of every release. The 50 most-downloaded entries are additionally install-verified in clean containers. Corrections: contact the desk.

