Skip to content

Commit 2f46ebc

Browse files
committed
Improve $ref explanations
Signed-off-by: Juan Cruz Viotti <jv@jviotti.com>
1 parent 86b862c commit 2f46ebc

2 files changed

Lines changed: 97 additions & 25 deletions

File tree

content/2020-12/core/id.markdown

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ related:
3232
The `$id` keyword explicitly turns a schema into a _schema resource_ (a schema
3333
that is associated with a URI). Relative URIs are resolved against the
3434
_current_ base URI, which is either the closest parent `$id` keyword
35-
(applicable in the case of compound schemas) or the base URI as determined by
35+
(applicable in the case of compound schemas), or the base URI as determined by
3636
the context on which the schema is declared (i.e. serving a schema over HTTP
3737
_may_ implicitly award it such URL as the base).
3838

content/2020-12/core/ref.markdown

Lines changed: 96 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -32,33 +32,105 @@ related:
3232
keyword: $defs
3333
---
3434

35-
The `$ref` keyword is used to statically reference a schema. This is useful for avoiding code duplication and promoting modularity when describing complex data structures.
36-
37-
{{<common-pitfall>}} Because of how URI resolution works, a reference to an
38-
absolute URI does not necessarily mean the reference points to a remote
39-
resource. Conversely, a reference to a relative URI does not necessarily mean
40-
the reference points to the current schema resource.
41-
42-
When encountering a reference, a JSON Schema implementation will first resolve
43-
it into an absolute URI given the base URI of the schema. If the resulting
44-
destination is present in the schema, it will be a local reference. Otherwise,
45-
a remote reference.
46-
{{</common-pitfall>}}
47-
48-
{{<learning-more>}} URIs play a central role in JSON Schema. Going through the
49-
URI [RFC 3986](https://datatracker.ietf.org/doc/html/rfc3986) specification is a
50-
must for gaining a deeper understanding of references, identifiers, and
51-
anchors. More specifically, we recommend carefully studying [URI
52-
resolution](https://datatracker.ietf.org/doc/html/rfc3986#section-5), URLs vs
53-
URNs, and the difference between a URI and a URI Reference.
54-
55-
Additionally, a JSON Schema reference URI may contain a JSON Pointer. For this
56-
reason, we recommend reading the JSON Pointer
57-
[RFC 6901](https://www.rfc-editor.org/rfc/rfc6901) specification, primarily its
58-
proposed [URI fragment identifier
35+
The `$ref` keyword enables a schema to reference another schema by its URI,
36+
effectively importing its keywords into the current evaluation process. This
37+
keyword is the cornerstone of schema composition, allowing complex schemas to
38+
be created out of simpler ones. A reference may set its URI fragment to a [JSON
39+
Pointer](https://www.rfc-editor.org/rfc/rfc6901) that determines the
40+
destination of the reference after first resolving the rest of the URI.
41+
42+
{{<common-pitfall>}}
43+
44+
**Avoid referencing other schema files using their file paths**. While some
45+
implementations support this by automatically constructing schema URIs that
46+
make use of the `file://` scheme, this is not enforced behaviour. The only
47+
standard and guaranteed mechanism of declaring a schema URI for identification
48+
and referencing purposes is through the [`$id`]({{< ref "2020-12/core/id" >}})
49+
keyword.
50+
51+
{{</common-pitfall>}}
52+
53+
{{<common-pitfall>}}
54+
55+
The target of a reference must be a schema. Referencing a JSON value that is
56+
not unambiguously recognised as a schema leads to undefined behaviour. This
57+
not only includes referencing arbitrary JSON files (the obvious case), but also
58+
referencing parts of a schema that a JSON Schema evaluator does not consider to
59+
be a subschema. For example, referencing the contents of the [`examples`]({{<
60+
ref "2020-12/meta-data/examples" >}}) keyword.
61+
62+
{{</common-pitfall>}}
63+
64+
References are either _internal_ (pointing at schemas within the same schema
65+
definition) or _external_ (pointing at schema resources outside the given
66+
schema definition). If the reference is a relative URI, it is resolved against
67+
the _current_ base URI, which is either the closest parent URI as set by the
68+
[`$id`]({{< ref "2020-12/core/id" >}}) keyword, or the base URI as determined
69+
by the context on which the schema is declared. Schema wrappers like OpenAPI
70+
are notable examples of the latter. A relative reference from a schema embedded
71+
in an OpenAPI specification is resolved from the root of the API specification,
72+
and not from the root of the schema.
73+
74+
{{<best-practice>}}
75+
76+
It is highly recommended to make use of _external_ references to break down
77+
complex monolithic schemas into smaller schema files. If you need a monolithic
78+
schema, you can automatically inline external references using the [`jsonschema
79+
bundle`](https://github.com/sourcemeta/jsonschema/blob/main/docs/bundle.markdown)
80+
command.
81+
82+
{{</best-practice>}}
83+
84+
Note that a reference to an absolute URI does not necessarily mean that the
85+
reference is external. Conversely, a reference to a relative URI does not
86+
necessarily mean that the reference is internal. When encountering any type of
87+
reference, a JSON Schema implementation will check if the root schema resource
88+
or its nested schema resources (if any) declare the canonically resolved
89+
version of such URI through keywords such as [`$id`]({{< ref "2020-12/core/id"
90+
>}}) and [`$anchor`]({{< ref "2020-12/core/anchor" >}}). If so, the reference
91+
is considered internal. This internal-first lookup is what enables the standard
92+
[bundling
93+
](https://json-schema.org/blog/posts/bundling-json-schema-compound-documents)
94+
process.
95+
96+
{{<learning-more>}}
97+
98+
If you are having a hard time understanding references and some of its more
99+
subtle scenarios (like base URI resolution), it is probably because you don't
100+
have a strong grasp of URIs yet (a notably hard but universal pre-requisite!).
101+
102+
To learn more about URIs, we strongly suggest studying the [IETF RFC
103+
3986](https://www.rfc-editor.org/info/rfc3986) URI standard. To avoid
104+
confusion, note that there is also a [WHATWG URL
105+
Standard](https://url.spec.whatwg.org) that targets URLs in the context of web
106+
browsers. However, JSON Schema only implements and expects the IETF original
107+
standard. As a notable extension, this keyword supports referencing specific
108+
parts of a schema through the use of a JSON Pointer, so we also recommend
109+
studying the [IETF RFC 6901](https://www.rfc-editor.org/info/rfc6901) JSON
110+
Pointer standard and its [URI fragment identifier
59111
representation](https://www.rfc-editor.org/rfc/rfc6901#section-6).
112+
60113
{{</learning-more>}}
61114

115+
To debug references and how JSON Schema is interpreting your specific relative
116+
URIs, try the [`jsonschema
117+
inspect`](https://github.com/sourcemeta/jsonschema/blob/main/docs/inspect.markdown)
118+
command. This command prints detailed information about each schema reference
119+
and of each location of the schema. For example:
120+
121+
```sh
122+
$ jsonschema inspect schema.json
123+
...
124+
125+
(REFERENCE) ORIGIN: /properties/foo/$ref
126+
Type : Static
127+
Destination : https://example.com/schemas/example#/$defs/uuid
128+
- (w/o fragment) : https://example.com/schemas/example
129+
- (fragment) : /$defs/uuid
130+
131+
...
132+
```
133+
62134
## Examples
63135

64136
{{<schema `Schema with a relative reference` >}}

0 commit comments

Comments
 (0)