0

Consider an API with an endpoint in which you pass a parameter foo as part of the URL-path, and pass some parameters as json in the body of a POST request.

  • This uri parameter foo must have one of the values fooBar and fooBaz. Otherwise the request gest a 404.
  • If the value of foo is fooBar then the body attribute bar is required.
  • If the value of foo is fooBaz then the body attribute baz is required.

How do I specify a jsonSchema for such an endpoint?


An example for such a request would be:

POST /path/to/endpoint/fooBar HTTP/1.1
Accept: application/json
Content-Type: application/json
Content-Length: 20
{"bar":"something"}

So far I came up with the following based on this and that but I have no idea if this is correct.

{
    "$schema": "http://json-schema.org/draft-03/schema#",
    "id": "http://json-schema.org/draft-03/schema#",
    "definitions": {
        "foo": { "enum": ["fooBar", "fooBaz"] }
    },
    "type": "object",
    "properties": {
        "foo": { "$ref": "#/definitions/foo" },
        "bar": { "type": "string" },
        "baz": { "type": "string" }
    },
    "links": [{
        "rel": "self",
        "href": "/path/to/endpoint/{foo}",
        "hrefSchema": {
            "properties": {
                "foo": {"$ref": "#/definitions/foo"}
            }
        }
    }],
    "anyOf": [
        {
            "properties": {
                "foo": { "enum": ["fooBar"] }
            },
            "required": ["bar"]
        },
        {
            "properties": {
                "foo": { "enum": ["fooBaz"] }
            },
            "required": ["baz"]
        },
    ]
}
7hibault
  • 2,371
  • 3
  • 23
  • 33

1 Answers1

1

JSON Schema validation is not aware of the URI that the data being validated came from (if any). You can use JSON Hyper-Schema to tell your users how to send that data in the way you expect, but you will still need to do an additional check on the server-side to validate that the request was sent properly.

Before I get into the solution, I want to point out a couple things. Your $schema is set to "draft-03". If you really need "draft-03", this solution won't work. anyOf, allOf and the array form of required were added in "draft-04". hrefSchema was added in "draft-06". Since "draft-06" is brand new and is not well supported yet and you don't need hrefSchema anyway, I'm going to assume "draft-04" ("draft-05" was effectively skipped).

The next thing to mention is that you are using id wrong. It should be the identifier for your schema. You usually don't need this, but if you do, it should be the full URI identifying your schema. That is how my solution uses it.

Last thing before I get into the solution. If you are using the link keyword, you are using JSON Hyper-Schema and your $schema should reflect that. It should have "hyper-schema" instead of "schema" at the end of the URI.

Now the solution. I broke it into two parts, the schema that you put through the validator and the hyper-schema that tells users how to make the request. You've got the first one right. I only fixed the $schema and id.

{
  "$schema": "http://json-schema.org/draft-04/schema#",
  "id": "http://example.com/schema/my-foo-schema",
  "type": "object",
  "properties": {
    "foo": { "enum": ["fooBar", "fooBaz"] },
    "bar": { "type": "string" },
    "baz": { "type": "string" }
  },
  "anyOf": [
    {
      "properties": {
        "foo": { "enum": ["fooBar"] }
      },
      "required": ["bar"]
    },
    {
      "properties": {
        "foo": { "enum": ["fooBaz"] }
      },
      "required": ["baz"]
    }
  ]
}

Next is the hyper-schema. You can't reference anything external (href, instance data) in a request schema, but you can write your schema so that it matches your href. The duplication is unfortunate, but that's the way you have to do it.

{
  "$schema": "http://json-schema.org/draft-04/hyper-schema#",
  "links": [
    {
      "rel": "http://example.com/rel/my-foo-relation",
      "href": "/path/to/endpoint/fooBar",
      "method": "POST",
      "schema": {
        "allOf": [{ "$ref": "http://example.com/schema/my-foo-schema" }],
        "properties": {
          "foo": { "enum": ["fooBar"] }
        }
      }
    },
    {
      "rel": "http://example.com/rel/my-foo-relation",
      "href": "/path/to/endpoint/fooBaz",
      "method": "POST",
      "schema": {
        "allOf": [{ "$ref": "http://example.com/schema/my-foo-schema" }],
        "properties": {
          "foo": { "enum": ["fooBaz"] }
        }
      }
    },
    {
      "rel": "self",
      "href": "/path/to/endpoint"
    }
  ]
}

Usually when I come across something that is difficult to model with hyper-schema, it means that I am doing something overly complicated with my API and I need to refactor. I suggest taking a few minutes to think about alternative designs where this switching on an enum isn't necessary.

Jason Desrosiers
  • 22,479
  • 5
  • 47
  • 53