Understanding and Using RESTful APIs
Representational State Transfer (REST) is a style of software architecture used in developing online services known as RESTful web services.
Representational State Transfer (REST) is a style of software architecture used in developing online services known as RESTful web services. It was introduced in 2000 by Roy Fielding in a PhD dissertation at UC Irvine entitled Architectural Styles and the Design of Network-Based Software Architectures. At that time, the HTTP protocol for online traffic was subject to ongoing standardization. HTTP 1.0 was introduced in 1996, and HTTP 1.1 was developed in 1996-99.
Fielding collaborated with a large number of developers throughout this standardization process to develop his own model of software architecture for web services. By honing this down with the help of his colleagues’ feedback, Fielding created REST, a set of properties, principles and constraints that underpin RESTful web services. In terms of RESTful APIs specifically, the API (Application Programming Interface) is just the surface layer of the underlying service. The API is what end users see and interact with; the front end that transmits inputted data to the programming code that forms the guts of the service.
REST helps with this process because it generally transmits very little data to complete the request, making it much more bandwidth-friendly than its predecessors like SOAP. For web services and especially customer-facing online services, RESTful APIs are a streamlined, standards-based way to make use of HTTP and other standard protocols for quick and effective transferral of data. Because of this, they have rapidly become the preferred method of allowing internet users to interact with web services and underpin many of the online transactions we all take for granted on a day-to-day basis.
This essential, everyday functionality is reinforced with robust response codes and error checking, along with support for a variety of different media types and very large resources, which can be paginated or partially loaded to prevent adverse impacts on web service performance. RESTful APIs sit as the intermediate layer between those resources and the end user, which is accessible via clients like web browsers for streamlined and intuitive interactions.
How does RESTful API work?
REST establishes a generic resource interface. This is achieved by constraining the interface between components to ensure seamless access, for example, to resources accessible via HTTP and others located on an FTP server. This constraint is a limitation – but a deliberate one – to allow access to many different services via a single proxy. Other architectures, if required, can be invoked as parallel processes, such as how online architecture handles mail traffic separately and distinctly from web traffic.
In the case of Kong, the API gateway can run in front of any RESTful API and uses plugins to add new services and extend its functionality. The Kong Admin API is itself a RESTful API and can be used to configure plugins that have been installed. Kong Enterprise also includes a dashboard GUI for managing the admin API. This internal admin API gives full control over your Kong installation — and as such, should always be kept secured against unauthorized public access.
REST has been called “the language of the internet” in a TechTarget definition, and it’s not hard to see why. As cloud computing deployments continue to increase, REST allows the development of web services that can communicate seamlessly with the cloud and is used by the biggest online brands including Google, Amazon and social networks like Twitter.
What Are the Specifications of REST?
Six constraints define a RESTful system — these are not limitations in the sense of reducing the potential of REST, but rather they are guidelines that establish certain expectations that should always be true of a RESTful system. Working within these constraints ensures that the benefits of a RESTful system are achieved, including simplicity, scalability and overall performance.
Cached responses can be stored and accessed at a later date (compare with statelessness below) so that web services can essentially pass information to themselves or to other loosely coupled services that work on the same data. Good management of cached data can ensure that it does not become stale or obsolete – for example, storing shopping cart data beyond a time when the user is realistically likely to return and complete the checkout process. Accessing cached data can eliminate the need for data to be transmitted between the client and the server, helping to streamline scalability and enhance performance.
Concepts like Service Oriented Architecture (SOA), microservices and web services divide monolithic application code into smaller, more manageable services that can be developed and updated independently from one another. Client-Server Architecture applies the same principles to the flow of data in use. It acknowledges that client-side processes and server-side processes might be quite different and independent from each other. By developing different client-side interfaces, web services can support access from different platforms and operating systems, improving portability while also allowing developer teams to evolve each part individually from the rest.
Code on Demand
Client-server interactions can be thought of like two connected cogs. One turns the other to achieve some output of motion. If you introduce further gears into the system, you can alter the nature of that output — its direction, speed and so on — without changing the fundamental fact that some motion is produced. RESTful systems adopt a similarly layered structure, allowing intermediate layers to be inserted to introduce, for example, security policies, proxies and load balancing capabilities, without interrupting the functionality of the system itself.
Statelessness is one of the most significant characteristics of RESTful web services and especially of RESTful APIs. It dictates that all of the information required by the service should be contained within the request and must not be stored by the server between requests. This is visible in online service requests as the parameters included after a question mark in the Uniform Resource Identifier (URI), which pass additional information to the service. Statelessness ensures that each time the service is used, it starts from the same default configuration and that the client can provide to it updated parameters via the client-facing RESTful API.
Finally, the uniform interface used in RESTful systems means there are consistencies in the architecture to make development easier.
These uniformities include:
- Resource identification in requests — RESTful web services identify individual resources, e.g. through the use of URIs.
- Resource manipulation through representations — Ensuring that a client-side representation of a resource, complete with metadata, is sufficient to modify or delete that resource.
- Self-descriptive messages — RESTful messages each contain sufficient information to allow the message to be processed, e.g. the type of parser that must be used.
Uniform interface is a fundamental characteristic of REST, decoupling the architecture in a way that allows it to evolve. The constraints as a whole are derived from other common architectural styles. For example, uniform interface helps to make RESTful APIs reusable; making them stateless ensures they are reliable while cacheability enables on-demand functionality.
What is the Usage of REST?
In his dissertation, Fielding defined the data elements of REST, including:
- Resources (The target of a hypertext reference)
- Resource Identifiers (URLs/URNs — now evolved into URIs)
- Representation (Visible output such as an image or web page)
- Representation Metadata (Media types, modification timestamps, etc.)
- Resource Metadata (Such as source links and alternates)
- Control Data (Cache control, ‘if modified’ tests, etc.)
Resources are the fundamental element in REST. Anything that can be named can be classed as a resource, whether it is an image, document, object or even a collection of other resources. They can be static, such as a published document which cannot change, or temporal, such as the latest version of a dynamic document or information that is necessarily associated with a point in time like the “current” weather conditions. By doing this, REST refers broadly to concepts, whether they are always the same or a specific version of a changing resource — and without unnecessarily limiting the variety of different objects and entities that can be named as resources.
Once a resource is defined, REST can use standardized methods such as those used in HTTP to obtain and manipulate data from that resource. These methods include:
- GET — Retrieves data from the target resource or URIs that link to the individual members within a collection resource
- POST — Creates a new member resource and receives the URI of that resource back via the Location header field
- PUT — Replaces the target resource or creates a new one if none already exists
- PATCH — Updates the existing instances of the resource or creates new ones as necessary
- DELETE — Deletes the specified resource
It is worth noting that GET is considered a “safe” method as it only reads the resource, whereas the other methods write data to the specified target. For this reason, GET is widely used in web services that only need to return information to the user, without giving them the ability to alter or edit that data. REST can connect a range of different entities. While it is common to talk about client-server interactions, the communication can be encapsulated in a variety of ways.
In addition to the client and the server, there are cache requests, along with resolvers like the Domain Name System (DNS) lookup library and tunnels like SSL and SOCKS. Again, these encapsulations enable some of the fundamental features of RESTful web services, such as the ability to store cached data and retrieve it later to pass information from one session to another. Resolvers ensure resource identifiers can be translated into network addresses, such as the way the DNS uses text names to substitute for the IP address of a particular server/website. Crucially, tunnels allow communication across boundaries like firewalls and network gateways, which is one element in allowing RESTful web services to communicate with enterprise applications via APIs.
What Are the Best Practices of RESTful APIs?
There are many best practices when working with RESTful APIs, some of which we’ve already touched on above. For example, the safe method GET can only be used to retrieve data and not to write it, which makes it an important method to use — or not use — as part of best practice for RESTful APIs.
Naturally, if you want to write data, you will use one of the other methods. POST, PUT and PATCH all share some common functionality but with subtle differences, so it is worth always keeping in mind which is the best option for a specific task you are trying to achieve. Also — and somewhat unusually for software developers — it is generally better to be concrete in API code, rather than abstract. This is simply a matter of being more descriptive and helps to ensure that your API code can be understood in the future if and when the time comes to revisit it and make changes.
Some best practices relate to the way resources are defined and particularly the names assigned to them, which should ideally be:
While any of these characteristics will not necessarily break the functionality of the API, it is easier to work with plural nouns, and using only lowercase removes any risk of encountering errors relating to case-sensitive instances.
There are circumstances where you might not want to use solely lowercase characters. For example, if your API needs to work with a JSON response type, then so-called camelCase is preferable. As its name suggests, camelCase typically begins and ends with lowercase, but contains uppercase characters in the middle — creating a camel-like “hump” in the string. In camelCase, only the initial word is written in lowercase. After that, each subsequent word starts with a capital with no spaces in between, helping to improve the readability of concatenated strings.
If for some reason you do not want to remove spaces between words completely by using camelCase, then it is generally considered preferable to replace spaces with hyphens, rather than using underscores or the %20 HTML character code for a space. Remember, resources are “things;” therefore, you should refer to them with nouns. As an example, use ‘products’ and not ‘addNewProduct’ in the API endpoint. You can then use the HTTP methods listed above as the verbs that manipulate that data, once it has been retrieved using the GET method.
Simple Searching and Sorting With GET
Searching, sorting, filtering and pagination of data does not require the data itself to be altered. Therefore, the GET method should be sufficient. You can achieve this by adding a query parameter to the URI transmitted via GET — for example, ?q= for search queries or ?s= for sorting.
Pagination is a useful additional example of best practice, as it allows large datasets to be divided into more manageable chunks, even if this is only done for the purposes of visually rendering that data for a human user to view. You can manage pagination using ‘offset’ and ‘limit’ parameters, as well as providing links to the next and previous page in the results.
HTTP Error and Status Codes
There are more than 70 potential status codes defined within the HTTP standard, and while not all of these will apply to every use case, it’s important to make good use of those that might arise from your particular API.
Some simple examples include:
- 2xx codes to report success
- 3xx codes to report redirection
- 4xx codes to report client error
- 5xx codes to report server error
Many of these codes are familiar even to novice computer users, such as 404 ‘not found’ errors when a web page is missing and 503 Service Unavailable notices. As such, you can capitalize on that by ensuring you make use of these status codes and error codes in your API, in the knowledge that even a casual computer user will understand what a 404 error is telling them if it occurs.
More HTTP Semantics
It’s not only status codes where it’s sensible to conform to HTTP semantics. Media types, also known as MIME types, specify the format of data and can include non-binary data for web services such as application/json and application/xml. You can include a Content-Type header in a GET request to ensure that the data retrieved is in the correct format or an Accept header to list several media types. If the data is not in the correct format, you will instead generate a 415 (Unsupported Media Type) error, and if the server cannot accept any of your specified types via an Accept header, it will return a 406 (Not Acceptable) error — both of which can be used for error handling in your API.
When requesting large resources such as high-resolution image files, it may be preferable to limit the size of the response. You can check whether this is possible using the Accept-Ranges header, combined with Content-Length, to determine the overall size of the resource you are trying to retrieve. A subsequent GET request can use Range to download only a specified number of bytes.
If the complete resource is required at a later point, the remainder can be downloaded, but by limiting the initial response to a partial size, you can put a limit on the amount of data transmitted and complete responses faster. This has been a principle of loading web-based resources from the early days of the internet, from jpeg images that load progressive layers of increasing resolution to more recent methods like “lazy” loading of images on the page as they come into view.
In RESTful web services, where speed is often a key consideration, partial responses allow the API to complete its request faster. This is good not only for end user perception but has real practical applications on slower connections, such as desktops still running on dialup internet connections and mobile devices still using 3G.
Finally, it’s sensible to give APIs a version number. This can form part of the URI used to access them, and it’s a good way to future-proof that access. It means that if at a later date you decide to launch a completely new API, it doesn’t have to mean immediately breaking access to the old version. You can launch the new version with its own unique URI and allow a period of grace — or a sunset period on the old API — before you switch off access completely.
There are many reasons why this could be preferable, from third-party APIs where you want to give external developers a chance to update their own code to use the new API, to troubleshooting where you want to be able to roll back to the old API if necessary. It also provides an immediate indication of how the current API version stands in the evolution of your services, and with web-based services, some changes will likely be introduced at some point, even if they are not anticipated in the short term. For example, a version number 0.x is an indication that the API has not officially launched yet but is perhaps in a public testing phase or limited beta. Incremental version numbers like 1.1, 1.2, etc. show that some updates have been made but that the API remains fundamentally the same.
Meanwhile, advancing the version number to 2.0 and beyond is taken as a sign of a generational change — for example, if you have rebuilt your API from scratch or introduced significant new functionality and/or switched off some major existing features. Some developers prefer to avoid dot notation completely and stick to ordinal numbers for their API versions, e.g., v1, v2, etc., to avoid any potential problems when including them in URIs. If you decide to do this, you reduce the likelihood of encountering issues, but you also limit yourself to only being able to announce “major” generational API updates — or at least that version updates will be seen as such by external end users.
Methods of Versioning
RESTful APIs offer several different ways to specify the version number, and the best option will depend on your individual use case.
Some examples include:
- Header Versioning — Sends the API version number as a Custom-Header, with the option to default to a specific version if this header is absent
- Query String Versioning — Specifies the API version number using a parameter in the query string, e.g., https://example.com/products/2?v=1
- URI Versioning — The version number is specified within the URI, e.g., https://example.com/v1/products/2
The different methods can have different implications on performance. For example, both Query String and URI versioning use the same string to refer to the same data every time, making them cache-friendly. In comparison, Header versioning can cause problems in large-scale deployments where the server-side cache can suffer from duplication issues by many clients accessing different versions of a web-based API. Because of this, you should consider using Query String or URI versioning except for use cases where you are confident server-side duplication will not become a problem.
Want to learn more?
Request a demo to talk to our experts to answer your questions and explore your needs.