This document serves as a proposal for how CJ might represent web service errors as JSON response-bodies.
- Example Swagger 2.0 User Schema
- Example Swagger 2.0 Error Schema
- Examples of HTTP 400 Bad Request (top level object)
- Examples of Bad Request (top level array; batch endpoint)
- Entire Request (no single field)
- i18n
A Swagger 2.0 schema for a User
looks like this:
"User": {
"type": "object",
"required": [
"fullName",
"emailAddress",
"tags"
],
"properties": {
"fullName": {
"type": "string",
"minLength": 4
},
"emailAddress": {
"type": "string",
"description": "must be unique across all users"
},
"birthday": {
"type": "number",
"description": "YYYYMMDD-formatted date, e.g. 20150601"
},
"tags": {
"type": "array",
"items": {
"type": "string",
"enum": [
"friendly",
"hostile",
"happy",
"sad"
]
}
}
}
}
A Swagger 2.0 schema for a Error
looks like this:
"Error": {
"type": "object",
"required": [
"code"
],
"properties": {
"field": {
"type": "string",
"description": "A simple JMESPath query, pointing to a single offending field"
},
"code": {
"type": "string",
"description": "An code explaining the nature of the error"
},
"description": {
"type": "string",
"description": "must be unique across all users"
}
}
}
Imagine a URL http://example.com/users
that, when POST'ed to, creates a single User
. To successfully create a User
, the inbound JSON must conform to the User
schema shown above. In addition to this, each User
s in our system must have a unique email address.
Example Request
HTTP 1.1 POST /users
{
"fullName": "Sally Smith",
"birthDate": 19820601,
"tags": []
}
Example Response
HTTP 400 Bad Request
{
"errors": [
{
"field": "emailAddress",
"code": "general.missing",
"description": "Email address was not provided"
}
]
}
An HTTP client attempts to create a User
but provides a full name that is too short:
Example Request
HTTP 1.1 POST /users
{
"fullName": "Sa",
"emailAddress": "[email protected]",
"tags": []
}
Example Response
HTTP 400 Bad Request
{
"errors": [
{
"field": "fullName",
"code": "general.tooShort",
"description": "Full name must be longer than 3 characters"
}
]
}
An HTTP client attempts to create a User
but provides a malformed email address and a full name that is too short:
Example Request
HTTP 1.1 POST /users
{
"fullName": "Sa",
"emailAddress": "asdasdasd",
"tags": []
}
Example Response
HTTP 400 Bad Request
{
"errors": [
{
"field": "fullName",
"code": "general.tooShort",
"description": "Full name must be longer than 3 characters"
},
{
"field": "emailAddress",
"code": "general.notValid",
"description": "Email address not valid"
}
]
}
An API client attempts to create a User
with an email address that would violate a uniqueness constraint:
Example Request
HTTP 1.1 POST /users
{
"fullName": "Sally Smith",
"emailAddress": "[email protected]",
"tags": []
}
Example Response
HTTP 400 Bad Request
{
"errors": [
{
"field": "emailAddress",
"code": "general.alreadyExists",
"description": "A user with that email address already exists"
}
]
}
An API client attempts to create a User
, providing an unsupported tag.
Example Request
HTTP 1.1 POST /users
{
"fullName": "Sally Smith",
"emailAddress": "[email protected]",
"tags": ["happy", "morose"]
}
Example Response
HTTP 400 Bad Request
{
"errors": [
{
"field": "tags[1]",
"code": "general.notInEnum",
"description": "\"morose\" is not a valid tag"
}
]
}
Imagine a URL http://example.com/users/batch
that, when POST'ed to, creates multiple User
s. To successfully create User
s, the inbound JSON must be a JSON array whose elements are JSON objects that conform to the User
schema shown above. In addition to this, each User
s in our system must have a unique email address.
Example Request
HTTP 1.1 POST /users/batch
[
{
"fullName": "Sa",
"emailAddress": "[email protected]",
"tags": []
},
{
"fullName": "Jimmy John",
"emailAddress" "delicious.sandw",
"tags": ["morose"]
}
]
Example Response
HTTP 400 Bad Request
{
"errors": [
{
"field": "[0].fullName",
"code": "general.tooShort",
"description": "Full name must be longer than 3 characters"
},
{
"field": "[1].emailAddress",
"code": "general.invalid",
"description": "Email address not valid"
},
{
"field": "[1].tags[0]",
"code": "general.notInEnum",
"description": "\"morose\" is not a valid tag"
}
]
}
Some requests failed to be processed not because of a particular field - but due to something higher level (e.g. maximum number of application key already provisioned for given developer). In that case, the field
property is omitted from the response.
Example Request
HTTP 1.1 POST /users
{
"fullName": "Sally Smith",
"birthDate": 19820601,
"emailAddress": "[email protected]",
"tags": []
}
Example Response
HTTP 400 Bad Request
{
"errors": [
{
"message": "Too many users created",
"code": "ad.users.tooMany"
}
]
}
CJ occasionally provides multi-language support in its APIs. As an alternative to providing a description
property whose value is an EN_US string, a code
property may be provided whose value corresponds to a key in our internationalization system (e.g. ad.advertiser.archiveUrl.length.exceeded
in cjo/content/src/main/resources/cj/cjo/language/content.properties
).
An HTTP client attempts to create a User
but does not send an email address:
Example Request
HTTP 1.1 POST /users
{
"fullName": "Sally Smith",
"birthDate": 19820601,
"tags": []
}
Example Response
HTTP 400 Bad Request
{
"errors": [
{
"field": "emailAddress",
"message": "Email address is missing",
"code": "ad.users.emailAddress.missing"
}
]
}
This is great. I suggest that we return an error code instead of a message--like your final example on localization. I suggest that the message displayed to a user is part of the app, not the resource. OTOH, it is nice to have something readable for troubleshooting so maybe we include an informational message, not considered part of the contract, along with the code.
The path language you show for identifying the problem data element is similar to JMESPath. We might want to standardize on that so we leverage both code and documentation resources already available.