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."
}