Define models from JSON-API Schema (RAML, OpenAPI, API Blueprint))


#1

In RAML one can specify the bodies of requests / responses by including a JSON Schema:

#%RAML 1.0
title: My API
version: v1
baseUri: http://example.com
mediaType:
  - application/json
types:
  Entity: !include schema.json#definitions/Entity
  PostEntity: !include schema.json#definitions/PostEntity
/entities:
  post:
    body:
      type: PostEntity
    responses:
      201:
        body:
          type: Entity

I’d like to conform my API to the JSON-API spec. However, I am not sure how to create my schema.json to describe the Entity and PostEntity bodies. Is there a way to reuse the JSON-API Schema and only specialize the data models (I’m thinking about the resource attributes in particular) or should I rewrite my schema from scratch to specify my data models and try conform to JSON-API ? If so, is there a way to test the conformance of my own schema, apart from testing payloads from my own schema against the JSON-API schema ?

I failed to find a good RAML (or API Blueprint or OpenAPI) + JSON-API workflow online. How and where do people usually define JSON-API models (payloads) ?


#2

Turns out that this seems to work.

In this schema I’m defining a success response Intenty that has a fixed type and a required myModelAttribute attribute:

{
  "$schema": "http://json-schema.org/draft-04/schema#",
  "title": "My API Schema",
  "id": "http://example.com/schema.json",

  "definitions": {
    "Entity": {
      "allOf": [
        {"$ref": "http://jsonapi.org/schema#/definitions/success"}
      ],
      "properties": {
        "data": {
          "allOf": [{"$ref": "http://jsonapi.org/schema#/definitions/resource"}],
          "required": ["attributes"],
          "properties": {
            "type": {
              "type": "string",
              "pattern": "Entity"
            },
            "attributes": {
              "type": "object",
              "required": ["myModelAttribute"],
              "properties": {
                "myModelAttribute": {
                  "type": "string",
                }
              }
            }
          }
        }
      }
    }
  },
  
  "anyOf": [{"$ref": "#/definitions/Entity"}]
}

The next JSON is both valid against the schema above and JSON-API:

{
  "data": {
    "type": "Entity",
    "id": "1",
    "attributes": {
      "myModelAttribute": "It works",
      "This": "is still a valid JSON-API payload",
    },
    "relationships": {
      "author": {
        "data": {"id": "42", "type": "people"}
      }
    }
  },
  "included": [
    {
      "type": "people",
      "id": "42",
      "attributes": {
        "name": "John",
        "age": 80,
        "gender": "male"
      }
    }
  ]
}

Definitely not the nicest way to to describe models, but at least it seems that I can work from a single source of truth for the API bodies…


#3

Unfortunately, this only works in the very simplest of interactions with a {json:api} service. Any use of the includes or fields parameters will render this invalid.

I guess you could create a coupling between a field in your service and its representations as serialized, and return a link to the aggregate schema in metadata. However, I’m not sure how you would create a static schema which could handle the dynamism of {json:api} correctly.