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 Checkers that can verify write requests based on any condition.

RESTHeart provides “out of the box” the jsonSchema Checker that validates the body of write requests against a JSON schema.

Compared to the internal MongoDB Json Schema validation, the jsonSchema Checker 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 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 checker simply define the collection  metadata property checkers as follows:

{
	"checkers": [{
		"name": "jsonSchema",
		"args": {
			"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.

Example

Create a collection enforcing the address JSON Schema

PUT /addresses HTTP/1.1

{
	"checkers": [{
		"name": "jsonSchema",
		"args": {
			"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
...

{
  "_warnings": [
    "#: 2 schema violations found",
    "#: required key [city] not found",
    "#: required key [country] not found"
  ],
  "http status code": 400,
  "http status description": "Bad Request",
  "message": "request check failed"
}

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 the following requests:

  • bulk PATCH
  • bulk POST that use update operators

To handle this requests, set the checker property skipNotSupported to false and add custom checkers to handle specific requests.

PATCH /addresses/*?filter={"country":"Italy"} HTTP/1.1

{ "updated": true }

HTTP 1.1 400 Bad Request

{
  "_warnings": [
    "the checker JsonSchemaChecker does not support this request and is configured to fail in this case. Note that the request is a bulk operation"
  ],
  "http status code": 400,
  "http status description": "Bad Request",
  "message": "request check failed"
}
POST /addresses HTTP/1.1

[{
	"address": "Via D'Annunzio, 28",
	"city": "L'Aquila",
	"country": "Italy"
   	"$currentDate": {
   		"timestamp": true
   	}
}]

HTTP 1.1 400 Bad Request

{
  "_warnings": [
    "the checker JsonSchemaChecker does not support this request and is configured to fail in this case. Note that the request uses update operators and it is a bulk operation"
  ],
  "http status code": 400,
  "http status description": "Bad Request",
  "message": "request check failed"
}