The Power of HTTP for REST APIs — PART 1
From the Basics Through Content and Language Negotiations
There are a variety of web frameworks and libraries that help us build and consume HTTP-based APIs quickly and easily. Whether you are new to HTTP or experienced, let’s remind ourselves of the power of HTTP to help us design robust REST-based APIs. We will lean heavily on the various HTTP-related specifications, so I will provide links to the specification where the topic is referenced in case you want to research the concepts further.
The Basics: URLs, HTTP Methods, and Headers
Uniform Resource Locators, or URLs, are an identification system for the location of resources on the Internet. URLs are powerful as they allow APIs to be referenced across any accessible server. Additionally, the URL’s path provides for a hierarchical structure, affording us the opportunity to organize our resources into logical groupings. It is the URL that often assigns API ownership (via the hostname) and resource structure (via the path).
HTTP methods indicate the action to take on a particular resource. Examples include: GET, POST, PUT, DELETE, and HEAD. The method requested informs the server whether we are requesting data or modifying data.
Summary: When we combine an HTTP method with a URL (sometimes called an endpoint), we inform an API server what kind of action to take and the resource to take the action upon.
Status codes, sometimes referred to as response codes, provide the client with a numeric code that indicates how the client should proceed based on the result of the request. Status codes are grouped into families, indicating if the request was a success (2xx), the request is being redirected (3xx), failed due to client error (4xx), or failed due to server error (5xx).
Summary: When properly implemented by API providers, consumers should first check the status code in the response to determine the result of the request. Our client code isn’t required to parse the response payload to determine if a success or failure message was returned.
Finally, HTTP headers are name/value pairs that may be passed from the client (as part of an HTTP request) to inform the server about the request and the client’s capabilities, or from the server (as part of an HTTP response) to inform the client about the results of the request that was processed. HTTP headers are designed to separate the details about the message from the message (the body of a request/response). The separation of concerns between the header and message prevents our API designs from mixing protocol-level concerns with the message payloads our API exchanges.
Summary: HTTP headers separate the client/server interaction from the API design. Doing so empowers API designers to focus on the API capabilities to be delivered rather than mixing protocol and resource representation within request and response payloads.
Building upon this foundational knowledge of HTTP, let’s next explore two specific headers that are useful for web-based APIs.
Content Negotiation: Supporting Multiple Content Types
Content negotiation allows clients to request specific content type(s) to be returned by the server. With content negotiation, we enable a single endpoint to support different types of resource representations (e.g. CSV, PDF, PNG, JPG, SVG, etc). While many APIs only offer one content-type, such as JSON, they may support multiple content types per endpoint, allowing consumers to select the best one based on the circumstance.
The client may request any content type it prefers using the Accept header, including specifying */* if the client can accept anything (common for browsers). Clients may suggest more than one content-type, using quality values (or “qvalues” for short) to indicate an order of preference. The API server will review the Accept header and return the response using the content type that matches both what the server supports and what the client requested. If the server cannot respond with an accepted content type, it will return a 415 Unsupported Media Type response code.
Let’s look at an example request:
GET https://api.example.com/projects HTTP/1.1 Accept: application/json;q=0.5,application/xml;q=1.0
Notice the use of the qvalue in the example above. The client accepts multiple content types: XML with a high preference and JSON with a lower (but also acceptable) preference. The qvalues provide a relative weight, or preference, for the content types is would like to have relative to the others listed. If a qvalue isn’t specified, the default weight assigned is 1.0. The use of qvalues allows our API client code to support a specific type, perhaps JSON for improved parsing performance, but XML is also acceptable in the case where the API provider only supports XML.
HTTP/1.0 200 OK Date: Tue, 16 June 2015 06:57:43 GMT Content-Type: application/xml
The server responds with the best match content type (assuming it has been coded to support one or both of these content types) using the Content-Type response header. We can use the same approach to add support for CSV and PDF content types for some or all of our endpoints.
Summary: Content negotiation extends our API beyond a single format, such as JSON or XML. It allows some or all endpoints of an API to respond with the content type that best meets the needs of the API consumer. We use the Accept header in our request to list the preferred content types. The server informs the client of the content type in the response payload using the Content-Type header, allowing the client to apply the proper parsing library to process the response.
Language Negotiation: Supporting Multiple Languages
Language negotiation allows APIs to support multiple languages. Similar to content negotiation, the API client sends the Accept-Language header to the server. If the API server supports multiple languages, it can return a response payload in one of the languages supported by the client, indicated by the Content-Language response header.
Note: When it comes to browser-based language negotiation, there can be situations that result in it not working properly. This has caused some API providers to shy away from using HTTP language negotiation.
Summary: If an API client knows which language a user prefers and the API supports it, we apply language negotiation to localize our API responses, including text, currency, etc.
Wrap-up — PART 1
In PART 1 we examined only a small portion of what is useful for our APIs from the HTTP specification. In PART 2, we’ll dive more into hypermedia APIs, HATEOAS, and caching.
Special thanks to Darrel Miller for reviewing this article.
DISCLOSURE STATEMENT: © 2019 Capital One. Opinions are those of the individual author. Unless noted otherwise in this post, Capital One is not affiliated with, nor endorsed by, any of the companies mentioned. All trademarks and other intellectual property used or displayed are property of their respective owners.