What is @stoplight/json-ref-resolver?
@stoplight/json-ref-resolver is a utility for resolving JSON references ($ref) in JSON documents. It helps in dereferencing and bundling JSON schemas or other JSON documents that contain references to other documents or internal references.
What are @stoplight/json-ref-resolver's main functionalities?
Dereferencing JSON References
This feature allows you to resolve internal JSON references within a document. The example shows how a reference to '#/bar' is resolved to the actual content of 'bar'.
const { resolve } = require('@stoplight/json-ref-resolver');
const document = {
"foo": { "$ref": "#/bar" },
"bar": { "baz": 123 }
};
resolve(document).then(result => {
console.log(result);
// Output: { foo: { baz: 123 }, bar: { baz: 123 } }
});
Resolving External JSON References
This feature allows you to resolve external JSON references by fetching the referenced document from a URL. The example shows how a reference to an external URL is resolved.
const { resolve } = require('@stoplight/json-ref-resolver');
const document = {
"foo": { "$ref": "http://example.com/bar.json" }
};
resolve(document).then(result => {
console.log(result);
// Output will depend on the content of http://example.com/bar.json
});
Custom Resolvers
This feature allows you to define custom resolvers for handling specific types of references. The example shows how to create a custom resolver for a 'custom://' scheme.
const { resolve } = require('@stoplight/json-ref-resolver');
const customResolver = {
canRead: (file) => file.url.startsWith('custom://'),
read: (file) => Promise.resolve({ custom: 'data' })
};
const document = {
"foo": { "$ref": "custom://example" }
};
resolve(document, { resolvers: [customResolver] }).then(result => {
console.log(result);
// Output: { foo: { custom: 'data' } }
});
Other packages similar to @stoplight/json-ref-resolver
json-schema-ref-parser
json-schema-ref-parser is a powerful library for parsing, resolving, and dereferencing JSON Schema $ref pointers. It supports both internal and external references and provides similar functionality to @stoplight/json-ref-resolver. However, it is more focused on JSON Schema specifically.
json-ref-lite
json-ref-lite is a lightweight library for resolving JSON references. It is simpler and has fewer features compared to @stoplight/json-ref-resolver, making it suitable for basic use cases where performance and simplicity are priorities.
json-refs
json-refs is another library for resolving JSON references. It provides a comprehensive set of features for both internal and external references, similar to @stoplight/json-ref-resolver. It also includes additional utilities for working with JSON references.
JSON Ref Resolver
Recursively resolves JSON pointers and remote authorities.
Features
- Performant: Hot paths are memoized, remote authorities are resolved concurrently, and the minimum surface area is crawled and resolved.
- Caching: Results from remote authorities are cached.
- Immutable: The original object is not changed, and structural sharing is used to only change relevant bits. example test
- Reference equality: Pointers to the same location will resolve to the same object in memory. example test
- Flexible: Bring your own readers for
http://
, file://
, mongo://
, custom://
... etc. - Reliable: Well tested to handle all sorts of circular reference edge cases.
Installation
Supported in modern browsers and node.
yarn add @stoplight/json-ref-resolver
Usage
All relevant types and options can be found in src/types.ts.
import { Resolver } from "@stoplight/json-ref-resolver";
const resolver = new Resolver(globalOpts);
const resolved = await resolver.resolve(sourceObj, resolveOpts);
Example: Basic Local Resolution
import { Resolver } from "@stoplight/json-ref-resolver";
const resolver = new Resolver();
const resolved = await resolver.resolve({
user: {
$ref: "#/models/user"
},
models: {
user: {
name: "john"
}
}
});
expect(resolved.result).toEqual({
user: {
name: "json"
},
models: {
user: {
name: "john"
}
}
});
Example: Resolve a Subset of the Source
This will resolve the minimal number of references needed for the given target, and return the target.
In the example below, the address reference (https://slow-website.com/definitions#/address
) will NOT be resolved, since
it is not needed to resolve the #/user
jsonPointer target we have specified. However, #/models/user/card
IS resolved since
it is needed in order to full resolve the #/user
property.
import { Resolver } from "@stoplight/json-ref-resolver";
const resolver = new Resolver();
const resolved = await resolver.resolve(
{
user: {
$ref: "#/models/user"
},
address: {
$ref: "https://slow-website.com/definitions#/address"
},
models: {
user: {
name: "john",
card: {
$ref: "#/models/card"
}
},
card: {
type: "visa"
}
}
},
{
jsonPointer: "#/user"
}
);
expect(resolved.result).toEqual({
name: "json",
card: {
type: "visa"
}
});
Example: Resolving Remote References with Readers
By default only local references (those that point to values inside of the original source) are resolved.
In order to resolve remote authorities (file, http, etc) you must provide readers for each authority scheme.
Readers are keyed by scheme, receive the URI to fetch, and must return the fetched data.
import { Resolver } from "@stoplight/json-ref-resolver";
import * as axios from "axios";
import * as fs from "fs";
const resolver = new Resolver({
readers: {
https: {
async read(ref: uri.URI) {
return axios({
method: "get",
url: String(ref)
});
}
},
file: {
async read(ref: uri.URI) {
return fs.read(String(ref));
}
}
}
});
const resolved = await resolver.resolve({
definitions: {
someOASFile: {
$ref: "./main.oas2.yml#/definitions/user"
},
someMarkdownFile: {
$ref: "https://foo.com/intro.md"
}
}
});
expect(resolved.result).toEqual({
definitions: {
someOASFile: {
},
someMarkdownFile: {
}
}
});
Example: Resolving Relative Remote References with the Authority Option
If there are relative remote references (for example, a relative file path ../model.json
), then the location of the source
data must be specified via the authority
resolve option. Relative references will be resolved against this authority.
import { Resolver } from "@stoplight/json-ref-resolver";
import * as fs from "fs";
import * as URI from "urijs";
const resolver = new Resolver({
readers: {
file: {
async read(ref: uri.URI) {
return fs.read(String(ref));
}
}
}
});
const sourcePath = "/specs/api.json";
const sourceData = fs.readSync(sourcePath);
const resolved = await resolver.resolve(sourceData, {
authority: new URI(sourcePath)
});
expect(resolved.result).toEqual({
user: {
}
});
In the above example, the user $ref will resolve to /models/user.json
, because ../models/user.json
is resolved against the authority of the current document (which was indicated at /specs/api.json
). Relative references will not work if the source document has no authority set.
Contributing
- Clone repo.
- Create / checkout
feature/{name}
, chore/{name}
, or fix/{name}
branch. - Install deps:
yarn
. - Make your changes.
- Run tests:
yarn test.prod
. - Stage relevant files to git.
- Commit:
yarn commit
. NOTE: Commits that don't follow the conventional format will be rejected. yarn commit
creates this format for you, or you can put it together manually and then do a regular git commit
. - Push:
git push
. - Open PR targeting the
develop
branch.