This section contains advanced topics for API design and development. These topics are intended for developers who are already familiar with the rest of the contract, and who are looking to add more advanced features. Implementing these should be strictly based on business requirements, and should not be added simply out of a sense of pedantic completeness.
This is the multi-page printable view of this section. Click here to print.
Advanced Topics
1 - Batch Operations
Batch operations are a way to bundle multiple API requests into a single HTTP request, reducing the number of round trips between the client and the server.
To accomplish this, we’re writing multiple fully self-contained MIME messages, and bundling them into a single HTTP
MIME message using the multipart/mixed
MIME type. Much like REST, batch requests do not guarantee a specific order of
execution, and do not provide a method for referencing between child requests. They should be route, version, and server
agnostic, and should not create a ‘spotty’ batch implementation which works for some resources, but not others.
As such, the path requirement is that all batch requests be submitted to a dedicated path at the server root,
notably /batch
. It is this endpoint’s responsibility to receive, dispatch, collect, and return batch operations.
Building a Batch Request
POST /batch HTTP/1.1
Authorization: Bearer <JWT Access Token>
Content-Type: multipart/mixed; boundary=batch_boundary
Content-Length: total_content_length
The header which differentiates a batch request from a regular request is the Content-Type
. By
using multipart/mixed
, we are able to use one HTTP request to bundle multiple HTTP requests, very
similar to attaching files to an email. These are separated by the boundary string.
Adding a Request to the Batch
--batch_boundary
Content-Type: application/http
Content-ID: <opaque_random_string>
# The raw http request is here. For example, this is a GET request.
GET /v1/resource_name/{id}
--batch_boundary-- # The trailing dashes indicates end of document
To add an HTTP request to a batch request, we surround it with the boundary string and include the following two headers:
Content-Type: application/http
- which indicates that the content is an HTTP request.Content-ID: <opaque_random_string>
- a unique identifier that allows a client to correlate a request with the eventual response.
Parsing a Batch Response
HTTP 1.1/200 OK
Content-Type: multipart/mixed; boundary=batch_boundary
Content-Length: total_content_length
A batch response, assuming it was properly authorized, should always return 200
. This indicates that the batch request
was properly received and processed, however, it makes no assumptions about the individual requests within the batch.
Extracting the Responses from a Batch
--batch_boundary
Content-Type: application/http
Content-ID: <opaque_random_string_1>
# The raw response MIME type is here.
HTTP 1.1/200 Ok
Content-Type: application/json
{ payload... }
--batch_boundary
Content-Type: application/http
Content-ID: <opaque_random_string_2>
# The raw response MIME type is here. For example, this is an error.
HTTP 1.1/404 Not Found
--batch_boundary
Content-Type: application/http
Content-ID: <opaque_random_string_2>
# The raw response MIME type is here. For example, this is an error.
HTTP 1.1/404 Not Found
--batch_boundary--
The response can be split using the --boundary
value, and individual content blocks can be extracted and parsed. In
the case where a Content-ID
was provided in the original request, the response body for this request will carry the
same Content-ID
.
Resource Timeouts
Any service that implements this needs to - by necessity - implement a request timeout for sub-requests. In the case
where a child request does not return within the timeout, the HTTP response should include a 504 Gateway Timeout
boundary for the affected request, however, the overall batch request should return with a 200
.
In alignment with existing research, the timeout for any sub-resource will be 1 second. If a request is canceled for this reason, the batch handler needs to inform the microservice that a request is canceled.
Request and Response Size Limits
While the HTTP Specification does not restrict requests and responses to specific sizes, your toolchain may impose restrictions. For example, use of the AWS API Gateway has a limit of 10MB. Rather than exhaust this payload, we apply the following limits on our batch requests:
- No more than 50 requests may be batched at a time.
- The total collated batch request cannot exceed 5MB in size.
- The total collated batch response cannot exceed 5MB in size.
- A single batched request cannot exceed 100KB in size.
- A response for a single batched request cannot exceed 100KB in size.
Error Cases
If the aggregate request exceeds the 5MB limit, or if more than 50 requests are made in a single batch, the batch
endpoint should handle no requests, and immediately return with 413 Entity Too Large
.
HTTP 1.1/413 Entity Too Large
All batched requests that do not exceed 100KB should be honored, while any batched requests that are too large should -
in their own response - return a 413 Entity Too Large
.
HTTP 1.1/200 Ok
Content-Type: multipart/mixed; boundary=batch_boundary
Content-Length: total_content_length
--batch_boundary
Content-Type: application/http
Content-ID: <opaque_random_string>
HTTP 1.1/200 OK
--batch_boundary
Content-Type: application/http
Content-ID: <opaque_random_string>
HTTP 1.1/413 Entity Too Large
--batch_boundary--
Authorization
Authorization for a batch request is handled at the batch level. The batch handler is required to validate the authorization, and then pass that authorization on to every child request that is made. It will not make assumptions about roles or permissions.
2 - Long Running Operations
All systems have SLO’s and SLA’s, and it’s not often possible to satisfy a request within the given constraints. There are also security concerns about timing attacks, and you may need to ensure that any given request is not being used to probe the details of your system. For this purpose, we offer the following behavior to standardize handling these kinds of events.
Deferred Responses
The first response to a long-running operation should be to accept the request. The following status codes may be used.
201 Created
- When a creation of a resource has been requested, but is not yet complete.202 Accepted
- When a modification to a resource is requested, but is not yet complete.
In both cases, the server should return a Location
header to inform the client where it may poll the results.
HTTP/1.1 202 Accepted
Location: https://api.example.com/v1/resources/deferred/<status-identifier>
Polling
Most clients will redirect to the Location
header automatically, though not all of them will continue to poll the
results. The initial result should be a 202 Accepted
response with a Retry-After
header to inform the client how
long it should wait before polling again. The body of the result should be an indicator on the status of the request.
HTTP/1.1 202 Accepted
Retry-After: 2
Cache-Control: no-store
{
"error": "not_ready",
"error_description": "A text description about how much longer it might take."
}
Successful Result
Once a request is complete, the server should return a 204 Found
response with no body, and a Location
header to
once again redirect the client to the final result.
HTTP/1.1 204 Found
ETag: "dd2796ae-1a46-4be5-b446-7f8c7a0e8342"
Last-Modified: "Wed, 21 Oct 2015 07:28:00 GMT"
Cache-Control: max-age=3600
{
... response body
}
3 - Internationalization
This section is still being developed. Please stay tuned!