JSON Schema Validation
Introduction
RESTHeart supports MongoDB schema validation to enforce a format to documents: rules-based validation from MongoDB 3.2 and Json Schema validation from MongoDB 3.6, more in the MongoDB documentation.
On top of this, RESTHeart provides a more general approach for validation based on Interceptors that can verify write requests based on any condition.
RESTHeart provides “out of the box” the jsonSchema Interceptor that validates the body of write requests against a JSON schema.
Compared to the internal MongoDB Json Schema validation, the jsonSchema Interceptor of RESTHeart provides additional advantages:
- schemas are stored in a special collection, the schema store
/_schemas
and are validated - schemas can and can be reused on multiple collections
- complex schemas can be defined using sub-schemas, i.e. using the $ref keyword
- documents can be validated using schemas that available online
- schemas are cached
JSON Schema
JSON Schema specifies a JSON-based format to define the structure of JSON data for validation, documentation, and interaction control. A JSON Schema provides a contract for the JSON data required by a given application, and how that data can be modified.
For more information about JSON Schema refer to json-schema.org; a very good resource is understanding json schema web site too.
The Schema Store and Schema resources
Schema Store resources allow store JSON schemas used for document validation; they are specialized collection resources whose documents must conform to the JSON schema format (specifically to the latest version draft-04)
Schema Store are first class citizens in RESTHeart and the format of
their URIs is /_schemas
Create the db schema store
PUT /_schemas HTTP/1.1
Schema resources are documents of the Schema Store. The following request creates a valid JSON Schema.
PUT /_schemas/address?wm=upsert HTTP/1.1
{
"$schema": "https://json-schema.org/draft-04/schema#",
"type": "object",
"properties": {
"address": { "type": "string" },
"city": { "type": "string" },
"postal-code": { "type": "string" },
"country": { "type": "string"}
},
"required": ["address", "city", "country"]
}
HTTP/1.1 201 Created
...
In JSON Schema, the id
property (not to be confused with the _id
key) has a special role. This property is auto-generated by RESTHeart as
can be noted in the response of the following GET request.
GET /_schemas/address HTTP/1.1
HTTP/1.1 200 OK
{
"$schema": "https://json-schema.org/draft-04/schema#",
"id": "https://schema-store/restheart/address#",
"_id": "address",
....
}
Document validation
To apply the jsonSchema simply define the collection
metadata property jsonSchema
as follows:
{
"jsonSchema": {
"schemaId": <schema_id> ,
"schemaStoreDb": <schema_store_db>
}
}
Property
|
Description
|
Mandatory
|
---|---|---|
schemaId |
the _id of the JSON schema to enforce |
Yes |
schemaStoreDb |
the db | No, if omitted the current db is used. |
MongoDB BSON types
This section provides an approach to validate the bson types used by MongoDB.
As an example, the following schema defines two bson types: date
and objectid
{
"_id": "bson",
"$schema": "http://json-schema.org/draft-04/schema#",
"definitions": {
"date": {
"type": "object",
"properties": {
"_$date": { "type": "number" }
},
"additionalProperties": false
},
"objectid": {
"type": "object",
"properties": {
"_$oid": { "type": "string" }
},
"additionalProperties": false
}
}
}
Once the definition has been saved in the schema-store
, the new types can be used by other schemas with the $ref
keyword
{
"_id": "post",
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"properties": {
"_id": { "$ref": "http://schema-store/restheart/bson#/definitions/objectid" },
"_etag": { "$ref": "http://schema-store/restheart/bson#/definitions/objectid" },
"title": { "type": "string" },
"content": { "type": "string" },
"date": { "$ref": "http://schema-store/bson#/definitions/date" }
}
}
Example
Create a collection enforcing the address JSON Schema
PUT /addresses HTTP/1.1
{
"jsonSchema": { "schemaId": "address" }
}
Now let’s try to create an invalid document.
Try to create an invalid address
POST /addresses HTTP/1.1
{ "address": "Via D'Annunzio 28" }
HTTP/1.1 400 Bad Request
...
{
"http status code": 400,
"http status description": "Bad Request",
"message": "Request content violates schema 'address': 2 schema violations found, required key [city] not found, required key [country] not found"
}
Passing valid data results in the document creation:
Create a valid documents
POST /addresses HTTP/1.1
{
"address": "Via D'Annunzio, 28",
"city": "L'Aquila",
"country": "Italy"
}
HTTP/1.1 201 Created
Limitations
jsonChecker does not support bulk PATCH requests:
To handle this requests, set the metadata property skipNotSupported
to true
, and add specialized interceptors to validate these requests if required.
PATCH /addresses/*?filter={"country":"Italy"} HTTP/1.1
{ "updated": true }
HTTP 1.1 501 Not Implemented
{
"http status code": 501,
"http status description": "Not Implemented",
"message": "'jsonSchema' checker does not support bulk PATCH requests. Set 'skipNotSupported:true' to allow them."
}