Static References — $ref
The $ref keyword references another schema or schema fragment, enabling reuse, modular composition, and external loading. JetValidator resolves $ref at compile time, generating optimized function calls or inlining the referenced schema directly.
Full Paths in Errors
JetValidator returns the complete path to a referenced schema in errors — from the origin all the way to the failure point:
const error = {
schemaPath:
"#/$ref/https://json-schema.org/draft/2020-12/schema/allOf/0/$ref/https://json-schema.org/draft/2020-12/meta/core",
};Path breakdown:
#/— the root schema$ref/https://json-schema.org/draft/2020-12/schema/— an external referenceallOf/0/— the first sub-schema inallOf$ref/https://json-schema.org/draft/2020-12/meta/core— another external reference
This full execution path makes it easy to trace exactly where validation failed, especially with deeply nested external references.
Reference Types
| Type | Keyword | Example | Use Case |
|---|---|---|---|
| Static Reference | $ref | { "$ref": "#/definitions/user" } | Reuse schema fragments |
| Dynamic Reference | $dynamicRef | { "$dynamicRef": "#meta" } | Polymorphic validation |
| Anchor Reference | $anchor + $ref | { "$ref": "#userAnchor" } | Named reference points |
| Dynamic Anchor | $dynamicAnchor + $dynamicRef | { "$dynamicRef": "#meta" } | Runtime scope resolution |
Dynamic references have their own page — see Dynamic References.
JSON Pointer References
JSON Pointers (RFC 6901) use / to navigate the schema structure: #/definitions/user points to schema.definitions.user, and # points to the root.
Basic definition reference
const schema = {
type: "object",
properties: {
user: { $ref: "#/definitions/user" },
admin: { $ref: "#/definitions/user" },
},
definitions: {
user: {
type: "object",
properties: {
name: { type: "string" },
email: { type: "string", format: "email" },
},
required: ["name", "email"],
},
},
};
const validate = jetValidator.compile(schema);
validate({
user: { name: "John", email: "john@example.com" },
admin: { name: "Alice", email: "alice@example.com" },
}); // âś… both use the same definitionDeep path references
const schema = {
type: "object",
properties: {
shippingAddress: { $ref: "#/definitions/addresses/shipping" },
billingAddress: { $ref: "#/definitions/addresses/billing" },
},
definitions: {
addresses: {
shipping: {
type: "object",
properties: {
street: { type: "string" },
city: { type: "string" },
zipCode: { type: "string", pattern: "^[0-9]{5}$" },
},
required: ["street", "city", "zipCode"],
},
billing: {
type: "object",
properties: {
street: { type: "string" },
city: { type: "string" },
country: { type: "string" },
},
required: ["street", "city", "country"],
},
},
},
};$defs (Draft 2019-09+)
$defs is the modern replacement for definitions:
const schema = {
$schema: "https://json-schema.org/draft/2020-12/schema",
type: "object",
properties: {
user: { $ref: "#/$defs/user" },
product: { $ref: "#/$defs/product" },
},
$defs: {
user: {
type: "object",
properties: {
id: { type: "integer" },
name: { type: "string" },
},
},
product: {
type: "object",
properties: {
id: { type: "integer" },
name: { type: "string" },
price: { type: "number" },
},
},
},
};JetValidator supports draft-06 through 2020-12. Use
definitionsfor Draft-07 and earlier,$defsfor 2019-09+.
Root reference (recursive)
$ref: "#" references the root schema, enabling recursive structures:
const schema = {
$id: "https://example.com/recursive-schema",
type: "object",
properties: {
name: { type: "string" },
children: {
type: "array",
items: { $ref: "#" }, // recursive
},
},
};
const validate = jetValidator.compile(schema);
validate({
name: "Root",
children: [
{ name: "Child 1", children: [{ name: "Grandchild", children: [] }] },
{ name: "Child 2", children: [] },
],
}); // âś… recursive treeLocal References
References within the same schema document need no external loading.
Shared validation rules
const schema = {
type: "object",
properties: {
username: { $ref: "#/definitions/identifier" },
groupId: { $ref: "#/definitions/identifier" },
userId: { $ref: "#/definitions/identifier" },
},
definitions: {
identifier: {
type: "string",
pattern: "^[a-zA-Z0-9_-]{3,20}$",
minLength: 3,
maxLength: 20,
},
},
};Recursive cross-references
A definition can reference itself for tree/graph structures:
const schema = {
type: "object",
properties: {
parent: { $ref: "#/definitions/node" },
},
definitions: {
node: {
type: "object",
properties: {
value: { type: "string" },
left: { anyOf: [{ type: "null" }, { $ref: "#/definitions/node" }] },
right: { anyOf: [{ type: "null" }, { $ref: "#/definitions/node" }] },
},
},
},
};
const validate = jetValidator.compile(schema);
validate({
parent: {
value: "root",
left: { value: "left-child", left: null, right: null },
right: { value: "right-child", left: null, right: null },
},
}); // ✅ binary treeCircular and recursive references never cause stack overflows in JetValidator — see Resolution & Loading.
External HTTP References
Load schemas from remote URLs. Requires a loadSchema callback and compileAsync (covered fully on the Resolution & Loading page).
const jetValidator = new JetValidator({
loadSchema: async (uri) => {
const response = await fetch(uri);
if (!response.ok) throw new Error(`HTTP ${response.status}: ${uri}`);
return response.json();
},
addUsedSchema: true, // cache fetched schemas
});
const schema = {
type: "object",
properties: {
address: { $ref: "https://api.example.com/schemas/address.json" },
},
};
const validate = await jetValidator.compileAsync(schema);
// Fetches address.json, then compiles
validate({ address: { street: "123 Main St", city: "Boston", zipCode: "02101" } });External references are resolved recursively — if a fetched schema references another remote schema, that one is fetched too. Local references (registered via addSchema) resolve directly without fetching, so a schema can freely mix local and remote $refs.
Fragments in external schemas
You can point to a specific part of an external schema using a JSON Pointer fragment. The remote schema is fetched once, and any referenced fragments are extracted from it:
const schema = {
type: "object",
properties: {
favoriteBook: {
$ref: "https://api.example.com/schemas/library.json#/definitions/book",
},
currentMagazine: {
$ref: "https://api.example.com/schemas/library.json#/definitions/magazine",
},
},
};
const validate = await jetValidator.compileAsync(schema);Anchors — $anchor
$anchor creates a named reference point. $ref: "#anchorName" references it. Anchors are resolved at compile time.
Local anchor reference
const schema = {
type: "object",
properties: {
primaryUser: { $ref: "#userSchema" },
secondaryUser: { $ref: "#userSchema" },
},
definitions: {
user: {
$anchor: "userSchema",
type: "object",
properties: {
name: { type: "string" },
email: { type: "string", format: "email" },
},
},
},
};
const validate = jetValidator.compile(schema);
// $anchor: "userSchema" creates the reference point;
// $ref: "#userSchema" resolves to itMultiple anchors
const schema = {
type: "object",
properties: {
user: { $ref: "#userType" },
admin: { $ref: "#adminType" },
},
definitions: {
user: {
$anchor: "userType",
type: "object",
properties: {
name: { type: "string" },
role: { enum: ["user"] },
},
},
admin: {
$anchor: "adminType",
type: "object",
properties: {
name: { type: "string" },
role: { enum: ["admin"] },
permissions: { type: "array" },
},
},
},
};External anchor fragments
An anchor in an external schema is referenced with <url>#anchorName:
const schema = {
type: "object",
properties: {
person: { $ref: "https://api.example.com/schemas/entities.json#personEntity" },
company: { $ref: "https://api.example.com/schemas/entities.json#companyEntity" },
},
};
const validate = await jetValidator.compileAsync(schema);Anchor and path resolve to the same target
An anchor name and the JSON Pointer path to the same schema resolve identically:
const schema = {
$id: "https://example.com/precedence",
type: "object",
properties: {
field1: { $ref: "#myAnchor" }, // via anchor
field2: { $ref: "#/definitions/explicit" }, // via path
},
definitions: {
explicit: {
$anchor: "myAnchor",
type: "string",
minLength: 5,
},
},
};
// Both references resolve to the same schema location$ref to a $dynamicAnchor
$ref can point to a schema marked with $dynamicAnchor, but it resolves statically — no dynamic scope behavior occurs. When both a $anchor and a $dynamicAnchor share a name in the current scope, $ref prefers the static $anchor. Scope and priority rules are detailed on the Dynamic References page.