Technical notes

SchemaVela

Schemas

What Is an API Schema? A Practical Guide to Request and Response Contracts

What an API schema constrains in a JSON message, how OpenAPI 3.1 relates to JSON Schema, and which decisions the schema file does not make.

An API schema is a written constraint on the shape of a message. It names the fields a sender may use, the types those fields take, and which names have to be present. It is not, by itself, the whole contract. Status codes, side effects, and the promise that an old client keeps working are separate statements. This note is about the schema layer. Which field changes stay compatible is the next question, and how a version records a compatibility promise is the one after that.

What the document is constraining

JSON, as RFC 8259 defines it, is a text format whose values are objects, arrays, numbers, strings, true, false, and null. A schema does not replace that format. It narrows which of those values are acceptable in one place.

The OpenAPI Specification 3.1.0 describes an HTTP API. In that specification, data types are based on JSON Schema Draft 2020-12, with integer added as a number that has no fraction or exponent. Models are Schema Objects. The specification says a Schema Object is a superset of that JSON Schema draft: the usual JSON Schema keywords apply, and the object may also carry other keywords.

Two version fields in the same file are easy to mix up. The openapi field is the version of the OpenAPI Specification the document uses. The info.version field is the version of the OpenAPI document, and the specification says that string is distinct from both the specification version and the API implementation version. Neither field, on its own, tells a client whether a change is safe to ignore. That judgment is the subject of versioning, not of the schema keywords.

Request schema and response schema

A useful description keeps the request and the response apart. They are different messages, often with different required fields. In OpenAPI the request body and each response can carry their own schema through a Media Type Object. Reusing one schema for both directions feels tidy and then fails the first time the server returns an id the client must not send.

The illustration below is not a captured message. It shows the split, not a live API.

Request, response, and error are separate schemas Three boxes. The request box lists sku and quantity. The response box lists id, sku, and quantity. The error box lists type and status. Request schema sku, quantity names the client sends Response schema id, sku, quantity names the server returns Error schema type, status not a flag inside 200

A document can hold all three. The boxes are a map of the split, not a message trace.

{
  "request": {
    "type": "object",
    "required": ["sku", "quantity"],
    "properties": {
      "sku": { "type": "string" },
      "quantity": { "type": "integer", "minimum": 1 }
    }
  },
  "response": {
    "type": "object",
    "required": ["id", "sku", "quantity"],
    "properties": {
      "id": { "type": "string" },
      "sku": { "type": "string" },
      "quantity": { "type": "integer" }
    }
  }
}

Illustration, not a captured message. The two objects are separate schemas.

The same split applies to failures. The shape of an error body is a schema too, and it should not be an undocumented variant of the success body.

There is a second “required” that is not the JSON Schema keyword. In OpenAPI, a parameter object has a boolean required. For a path parameter that boolean is required and must be true. For other parameters it defaults to false. A request body object has its own boolean required, defaulting to false. Those booleans say whether the parameter or body has to appear. The JSON Schema required array says which property names have to appear inside a JSON object. Mixing the two up produces a document that looks strict and is not.

Required, optional, and null

In JSON Schema Draft 2020-12, an object is valid against required when every name in that array is a property of the instance. The keyword is about presence of the name. It does not, by itself, say that the value is non-null, non-empty, or inside a range. Those are other keywords (type, minLength, minimum, and so on).

Absence and null are different JSON values. RFC 8259 includes null as a value. A schema that says "type": "string" does not accept null unless null is also an allowed type. A client that omits a field and a client that sends the field as null are making two different statements. If both are meant to be accepted, the schema has to say so on purpose.

Listing a name under properties does not forbid other names. JSON Schema’s additionalProperties keyword is what constrains properties that properties and patternProperties did not already cover. Setting it to false rejects unknown names. Leaving it off does not. That default is useful when a response may grow, and it is a trap when a receiver wanted a closed list and never wrote the keyword. What to validate, and where comes back to this, because a validator can only enforce the keywords that are actually in the schema.

Enums and unknown values

The enum keyword lists the values an instance may take. It is a closed list for validation: a value outside the list fails that schema. It does not say whether a future document may add a value, or what an old client should do when it sees one. Those are compatibility decisions.

If consumers treat the list as exhaustive in their own code, adding a value is not a free change even though the old messages still validate. Record that assumption next to the enum. The compatibility note walks through how response enums stay compatible with old clients.

What the schema does not decide

A schema can be precise and still leave the contract unfinished.

Table 1. What a schema states, and what it leaves open
Question Where it is answered Typical mistake
Which property names and types are allowed? Schema keywords such as type, properties, required, enum Treating an example value as if it were a constraint
May unknown property names appear? additionalProperties, when it is present Assuming properties closes the object
Which HTTP status is returned? The API contract, not the schema keywords Hiding the error inside a 200 response with a boolean flag
Will an old client survive a new field? An explicit compatibility rule Inferring the rule from info.version alone

The middle column is a classification used by this desk. Only the schema keywords are defined by the cited JSON Schema and OpenAPI documents. The compatibility and status rows are not measurements.

OpenAPI allows an example on a Schema Object, and marks that property deprecated in favor of the JSON Schema examples keyword. An example shows one valid-looking instance. It does not add a constraint unless a keyword does. Reviewers should not “read” a required field out of an example that happens to include it.

A field checklist before you publish a schema

Use this before a schema is treated as something clients can build against. It is a review list, not a score.

  1. The request schema and each response schema, including the error body, are separate objects where their fields differ.
  2. Every name in required is also described, and the description says what absence means.
  3. Fields that may be JSON null allow null in their type. Fields that may be omitted are simply not required.
  4. additionalProperties: false is present only where unknown names must fail. Otherwise the document does not pretend the object is closed.
  5. Each enum says whether new values are expected, and where that decision is recorded.
  6. OpenAPI parameter and request-body required booleans are not confused with the schema required array.
  7. info.version is not used as a substitute for a compatibility note.
  8. Examples are labeled as examples. They are not the only definition of the type.

Open questions

A schema document does not tell you when to run it. Some teams validate only in tests. Some validate every request in production and reject unknown fields. Both can be consistent with the same file. The file becomes a contract only after the publisher says which failures a client will actually see, which is the point of when a schema is enforced.

Tags: JSON SchemaOpenAPI

Sources

  1. OpenAPI Specification 3.1.0 — OpenAPI Initiative
  2. JSON Schema Validation, Draft 2020-12, section 6.5.3 (required) — JSON Schema
  3. JSON Schema Core, Draft 2020-12, section 10.3.2.3 (additionalProperties) — JSON Schema
  4. RFC 8259, The JavaScript Object Notation (JSON) Data Interchange Format — IETF