3
Designing a programming interface

This chapter covers

  • Transposing API goals into a programming interface
  • Identifying and mapping REST resources and actions
  • Designing API data from concepts
  • Differentiating between REST APIs and the REST architectural style
  • Why the REST architectural style matters for API design

In the previous chapter, you learned how to identify an API’s goals—what users can achieve using it. For a Shopping API, some of these goals could be search for products, get product, add product to cart, check out cart, or list orders. These goals form the API’s functional blueprint that we will use to design the actual programming interface that is consumed by its users (developers) and their software. To design this programming interface, we will transpose these goals and their inputs and outputs according to an API style, as shown in figure 3.1.

03-01.png

Figure 3.1 REST programming interface for the get product goal

REST stands for Representational State Transfer. Here, the REST API style transposes the get product goal into a programming interface. It is represented by a GET /products/{productId} request, where productId is an input parameter (here its value is P123), and a 200 OK is a response with some output data consisting of the reference, name, and price properties. How do we design such a programming interface?

Representing goals using REST or any other type of programming interface requires that you first understand how it works. Indeed, what do these GET, /products/{productId}, or 200 OK mean, for example? Without knowing that, you will be unable to actually design such a programming interface. Once you have some basic knowledge, you can analyze the goals and represent them according to the chosen API style. You also have to design data exchanged through the API more accurately than what we did when filling in the API goals canvas. The process is similar to what you do when programming.

That sounds quite straightforward, doesn’t it? But things are not always that simple. After learning how to transpose basic goals to a programming interface, you might realize that some of your goals cannot be represented easily. In such cases, you have to find a path between user-friendliness and compliance with the chosen API style in order to come up with the best possible representation.

After all this, you may wonder what Representational State Transfer actually means and why it has been chosen as the main example programming interface for this book. To teach API design, why use REST APIs? Why are these APIs better than others? Although they are widely adopted, there’s a far more important reason behind this choice: REST APIs are based on the REST architectural style, which relies on solid foundations that are useful to know when designing any type of API. We’ll get to that soon, but first things first. Let’s talk about some basic REST API principles.

3.1 Introducing REST APIs

To gain sufficient knowledge about REST APIs in order to actually design one, we will analyze the REST API call made by a consumer of the Shopping API to get a product’s information, as seen in this chapter’s introduction (figure 3.1). We’ll take for granted that the REST representation of this goal is GET /products/{productId}, and we will work on the GET /products/P123 example. If you remember section 1.1.1, you should guess that this request has something to do with the HTTP protocol. This analysis will show us how HTTP is actually used by this call. After that, we will be able to dive into the HTTP protocol and the basic principles of REST APIs.

3.1.1 Analyzing a REST API call

What happens when a consumer wants to complete the goal get product? Or, more specifically, what happens when they want to get detailed information about a product with the ID P123 from the products catalog using the REST Shopping API? Consumers have to communicate with the server hosting the API using the HTTP protocol, as shown in figure 3.2.

03-02.png

Figure 3.2 A REST API call using the HTTP protocol

Because this goal is represented by GET /products/{productId}, the consumer has to send a GET /products/P123 HTTP request to the Shopping API server. In reply, the server returns an HTTP response containing 200 OK, followed by the requested product’s information. (Note that this HTTP exchange has been simplified in order to focus only on the elements that matter to us.)

The request is composed of the GET HTTP method and the /products/P123 path. The path is an address identifying a resource on the server; in this case, the P123 product in products. The HTTP method indicates what the consumer wants to do with this resource: GET means that they want to retrieve the resource. From a functional perspective, such a request means something like, “Hi, can I have the information on the product whose ID is P123?” But from the HTTP protocol’s perspective, it means, “Hi, can I have the resource identified by the /products/P123 path?”

The first part of the response is composed of the 200 HTTP status code and its OK reason phrase. The HTTP status code tells us how the processing of the request went. Thanks to the reason phrase, we can guess that the 200 HTTP status code means that everything went OK. The second part of the response is called the response body. It contains the content of the resource identified by the path in the request, which, in this case, is the P123 product’s information represented as JSON data.

From a functional perspective, the response returned by the API server basically means, “Sure, here’s the requested product’s information.” From the HTTP perspective, it means, “No problem, here’s the requested resource’s content.”

Now you know how consumers can call the Shopping API to achieve the get product goal. But the HTTP protocol is not made just to retrieve JSON documents.

3.1.2 Basic principles of HTTP

HTTP is the foundation of communication for the World Wide Web. It is a programming language-agnostic protocol that is designed to allow the exchange of documents (also called resources) between applications over the internet. HTTP is used by a wide range of applications, the best known of which are web browsers.

A web browser uses HTTP to communicate with a web server hosting a website. When you type a URL (such as http://apihandyman.io/about) in the browser’s address bar, it sends a GET /about HTTP request to the server hosting apihandyman.io, just like when an API consumer sends a request to a REST API server. The response sent by the server contains a 200 OK HTTP status followed by the HTML page corresponding to the URL.

Browsers use the protocol to retrieve any type of resource (document): HTML pages, CSS files, JavaScript files, images, and any other documents that are needed by the website. But that’s not its only use. When you upload a photo to a social networking website, for example, the browser uses the HTTP protocol, but this time to send a document to a server. In this case, the browser sends a POST /photos request with a body containing the image file. The HTTP protocol can therefore also be used to send the content of a resource.

HTTP requests and responses always look the same regardless of what is requested and what is the result of the processing of the request (figure 3.3).

03-04.png

Figure 3.3 The basic structure of an HTTP request and response

Whatever its purpose, a basic HTTP request contains an HTTP method and a resource’s path. The HTTP method indicates what is to be done with the resource identified by the path. You have already seen two HTTP methods—GET to retrieve a resource and POST to send one—and you will discover more later in this chapter.

This first part of the request can be followed by a body containing the content of the resource that needs to be sent to the server to create, update, or replace a resource, for example. This content can be of any type: a JSON document, a text file, or a photo, for example.

As mentioned previously, the HTTP response returned by the server always contains a status code and explanatory reason phrase. This indicates how the processing of the request went—if it was a success or not. You have so far seen only one HTTP status code, 200 OK, but you’ll discover more later in this book (like the well-known 404 NOT FOUND that will be explained in section 5.2.3). This first part of the response can be followed by a body containing the content of the resource that was manipulated by the request. Like the request’s body, this content can be of any type.

The HTTP protocol seems quite simple. But are REST APIs that use this protocol that simple?

3.1.3 Basic principles of REST APIs

You have seen that using the REST Shopping API for the get product goal, consumers have to send an HTTP request to the server hosting the API. This request uses the GET HTTP method on the /products/{productId} path that identifies the product. If everything is all right, the server returns a response that contains an HTTP status indicating that, along with the product’s data. You also saw that if a web browser wants to retrieve a page from my apihandyman.io blog, it sends an HTTP request. This request uses the GET HTTP method on the page’s path (/about, for example). If everything is all right, the web server also returns a response containing a 200 OK HTTP status and the page’s content. Exactly the same thing happens!

Both the web server and the Shopping API server expose an HTTP interface that respects the HTTP protocol’s expected behavior. A basic REST API not only uses the HTTP protocol, it totally relies on it, as shown in figure 3.4.

03-05.png

Figure 3.4 Mapping a goal to an HTTP request

To let its consumers achieve their goals, a REST API allows them to manipulate resources identified by paths using standardized HTTP methods. A resource is a functional concept. For example, /products/{productId} identifies a specific product in the products catalog. This path identifies a product resource. The GET HTTP method represents the retrieve action that can be applied to this resource to actually get the product.

A REST API call is nothing more than an HTTP call. Let’s see now how we get from the get product goal to GET /products/{product’s reference}—how we transform goals to HTTP method and path pairs.

3.2 Transposing API goals into a REST API

You have discovered that a REST API represents its goals using the HTTP protocol. Goals are transposed into resource and action pairs. Resources are identified by paths, and actions are represented by HTTP methods. But how do we identify these resources and actions? And how do we represent them using paths and HTTP methods?

We do what has always been done in software design. We analyze our functional needs to identify resources and what happens to them before transposing them into a programmatic representation. There are many software design methods that you could use to identify resources and what you can do with them based on some specifications, like the API goals canvas from the previous chapter. This book, however, shows a very simple method in four steps (see figure 3.5).

03-06.png

Figure 3.5 From goals to REST API

First, we have to identify resources (functional concepts) and their relationships (how they are organized). Then we have to identify for each resource the available actions and their parameters and returns. Once this is done, we can proceed to the actual HTTP programming interface design by creating resources paths and choosing HTTP methods to represent actions.

In the following sections, we’ll walk through this process in more detail. Here, we only talk about the nominal case, when everything is 200 OK. We will talk about error handling in section 5.2.

3.2.1 Identifying resources and their relationships with the API goals canvas

The API goals canvas you discovered in chapter 2 describes who the users are, what they can do, how they do it, what they need to do it, and what they can get in return. We can use this information to identify the API goals that we will transpose into a REST API. To practice on a basic but complete example, I have enhanced the manage catalog part of the Shopping API goals canvas we began working with in chapter 2 (figure 3.6).

03-07.png

Figure 3.6 The API goals canvas for the manage catalog what

As you can see in this figure, when managing the product catalog, admin users can add a product to the catalog. They can also retrieve a product’s information and update, replace, or delete it. Finally, it’s possible to search for products using a free query. Figure 3.7 shows how we simply analyze the API goals and list all the nouns to which the goals' main verbs apply for identifying resources.

03-08.png

Figure 3.7 Identifying resources

When we add a product to the catalog, the main verb is add, and it is applied to both the product and catalog resources. When we get a product, the main verb is get, and it is applied to the product only. But when we search for products in the catalog using a free query, free query is a noun, not a resource, because the search verb does not apply to it directly. Therefore, we can identify two resources: catalog and product.

Now let’s see how these resources are related. To discover the resources' organization, we list goals mentioning more than one resource (see figure 3.8).

03-09.png

Figure 3.8 Identifying resource relationships

We have two goals dealing with more than one resource. In the first one, we add a product to the catalog. In the second one, we search for products in the catalog.

Resources may or may not have relationships to other resources, and a resource can contain some other resources of the same type. Such a resource is called a collection resource or simply collection. In our case, the catalog resource is a collection: it contains product resources. If we were designing an API related to city planning, a city could be a collection resource containing many building resources. A resource can also be linked to other resources; for example, a building resource could be linked to an address resource.

We have identified our catalog and product resources and how they are related. What can we do with them?

3.2.2 Identifying actions and their parameters and returns with the API goals canvas

A REST API represents its goals through actions on resources. To identify an action, we take the goal’s main verb and link it to the resource to which it applies, as shown in figure 3.9.

03-10.png

Figure 3.9 Identifying actions

This is straightforward for goals with a single resource, such as get product, where the verb get applies to the product resource. But what about the goals add product to catalog and search for products in catalog using a free query? Do we link add and search to product or catalog? We add a product to the catalog and we search for products in the catalog; in this use case, add and search are linked to the catalog resource. That means we link the verb to the main resource (catalog) that is used or modified—the other one (product) is only a parameter or a return.

These actions might need some additional parameters and can return some information. Fortunately, we’ve already identified these parameters and returns; the API goals canvas comes with a complete list of inputs (parameters) and outputs (returns), as shown in figure 3.10.

03-11.png

Figure 3.10 Identifying action parameters and returns

We just need to filter the inputs because some of them can be resources to which the action is applied. When we apply add to the catalog resource, we need some product information as an input; and, in return, we get the newly created product resource. We provide a free query to the search action that is applied to the catalog resource, and this action returns the matching product resources. We then do exactly the same thing for actions applied to the product resource, and we’re done! We have identified all the resources and their actions, including parameters and returns, by analyzing the API canvas and its goals (figure 3.11).

03-12.png

Figure 3.11 All the identified resources and actions

As you can see, this process is not really different from what you do when you design the implementation of software. It takes a long time to describe how to do this in a book, but actually doing it takes only a couple of minutes with the API canvas. Let’s now see how we represent all of this with the HTTP protocol. We will start by representing resources with paths.

3.2.3 Representing resources with paths

By analyzing the API goals canvas, we have identified two resources: catalog and product. We also have discovered that the catalog resource is a collection of product resources. How can we design these resources' paths? Figure 3.12 shows how to do so.

03-13.png

Figure 3.12 A REST resource’s path. The only requirement is that it must be unique, but it should also be explicit.

A REST resource’s path only needs to be unique. To identify the catalog, we could use a /c path. For products, we could use the product reference or technical ID and build a /{productId} path with it (/P123, for example). Such variables in paths are called path parameters. The /c and /{productId} paths are perfectly valid REST paths because they are unique.

But let’s be frank. What would you think about such an interface in the real world? This is not really consumer-friendly; and, remember from chapter 2, we should always design an API for its users. It would be better to choose paths that indicate explicitly what they represent. Why not simply use /catalog for the catalog resource and /product-{productId} for the product resource? That sounds good, but these paths are not the only possibilities, as figure 3.13 shows.

03-14.png

Figure 3.13 A REST resource’s path should expose hierarchy and type.

To improve user-friendliness, the relationship between the catalog and product resources could be reflected in the paths like in the folder hierarchy you find on a filesystem. Each product resource is an item in the catalog collection identified by /catalog, so we could choose the path /catalog/{productId} to represent a product. We could also explicitly indicate that a catalog is a collection of product resources by using a /products path, with a product from this collection being represented by the /products/{productId} path. That’s a lot of options! Figure 3.14 shows them all.

03-15.png

Figure 3.14 Choosing your resource path format

From a pure REST perspective, all of these representations are valid. Even if we’ve already discarded cryptic paths such as /c and /{productId} because they are obviously not consumer-friendly, we still have many possibilities. A catalog resource could be represented by /catalog or /products and a product resource by /product-{productId}, /catalog/{productId}, or /products/{productId}.

Although there are no official REST rules regarding resource path design (apart from uniqueness), the most widely adopted format is /{plural name reflecting collection’s item type}/{item id}. Using resource paths exposing resource hierarchy and using plural names for collections to show the collection’s item type has become a de facto REST standard.

In our example, a catalog should therefore be identified by /products and a product by /products/{productId}. This structure can be extended to multiple levels as in /resources/{resourceId}/sub-resources/{subResourceId}.

We’re almost done! We have identified resources and their actions, and we have designed our resource paths. Here comes the final step, representing actions with the HTTP protocol.

3.2.4 Representing actions with HTTP

Let’s start with the catalog resource and its add action, as shown in figure 3.15.

03-16.png

Figure 3.15 Add product to catalog as an HTTP request

The HTTP representation of the goal add product to catalog is POST /products. When we add a product to the catalog resource identified by /products, we actually create a product resource using the provided product information. The HTTP method corresponding to the creation of a resource is POST. A POST request’s parameters are usually passed in the request body, so the product information parameter goes there. Once the product resource is created, the action should return the newly created resource identified by its path: /products/{addedProductId}.

Now, what is the HTTP representation of the search action of the catalog resource? The HTTP representation of search for products in catalog using a free query is GET /products?free-query={free query}, as shown in figure 3.16.

03-17.png

Figure 3.16 Search for products in catalog using a free query as an HTTP request

When we search for products, we want to retrieve them, so we have to use the GET HTTP method on the /products path. To only retrieve products matching some query, like a product name or partial description, we need to pass a parameter to this request. A GET HTTP request’s parameters are usually provided as query parameters in the path, as demonstrated in the following listing.

Listing 3.1 Query parameter examples

GET /example?param1=value1&param2=value2
GET /products?free-query=something

The parameters are located after the ? at the end of the path and provided in name=value format (param1=value1, for example). Multiple query parameters are separated by &. Once the search is done, the GET request returns the resources matching the path (which includes the free-query parameter).

We have represented all of the catalog resource’s actions, so let’s work on the product resource. We start with get product, which is relatively easy to represent as an HTTP request, as illustrated in figure 3.17.

03-18.png

Figure 3.17 Get product as an HTTP request

We want to retrieve the product resource identified by /products/{productId}, so we again use the GET HTTP method. The HTTP representation is therefore GET /products/{productId}. A GET resource always returns the resource corresponding to the provided path, so this action returns the content of this resource.

Now it’s time to discover new HTTP methods! How do we represent delete product with the HTTP protocol? The HTTP representation of this goal is simply DELETE /products/{productId}, as shown in figure 3.18.

03-19.png

Figure 3.18 Delete product as an HTTP request

The DELETE HTTP method’s purpose is obviously to delete the resource matching the provided path. In our use case, this action does not return any information.

Deleting a product was easy. Now can you guess what HTTP method we will use to update a product? There’s a trap here—the HTTP representation of update product is PATCH /products/{productId}, not UPDATE /products/{productId}, as shown in figure 3.19.

03-20.png

Figure 3.19 Update product as an HTTP request

The PATCH HTTP method can be used to partially update a resource. Like POST, the request parameters are passed in the request’s body. For example, if you want to update a product’s price, you can use PATCH on the product resource and pass the updated price in the body. In our use case, this action does not return any information.

Our last example illustrates an HTTP method that has two purposes. The HTTP representation of replace product is PUT /products/{productId}, as illustrated in figure 3.20.

03-21.png

Figure 3.20 Replace product as an HTTP request

The PUT HTTP method can be used to totally replace an existing resource or to create a nonexisting one and provide its identifier. In the latter case, it has the same effect as the add product to catalog action. Like POST, the request parameters are passed in the request’s body. In our use case, this action does not return information, but if you use PUT for creating a resource, the created resource should be returned.

So, the POST, GET, PUT, PATCH, and DELETE HTTP methods essentially map the basic CRUD functions (create, read, update, delete). Do not forget that these actions are made from the consumer’s perspective; for example, if you DELETE /orders/O123, it does not mean that the order O123 will actually be deleted from the database containing the orders. Such actions might simply update this order status to CANCELED.

These CRUD HTTP methods also have to be used to represent more than or not so CRUD actions. It can sometimes be difficult for beginning REST API designers (and sometimes even seasoned ones) to choose which HTTP method matches an action that doesn’t obviously map to a CRUD function. Table 3.1 shows some examples of actions that can help you see beyond CRUD.

Table 3.1 HTTP methods beyond CRUD

HTTP method Action
POST (and PUT in creation) Create a customer, add a meal to a menu, order goods, start a timer, save a blog post, send a message to customer service, subscribe to a service, sign a contract, open a bank account, upload a photo, share a status on a social network, and so on
GET Read a customer, search for a French restaurant, find new friends, retrieve opened accounts for the last 3 months, download a signed contract, filter best selling books, select black-and-white photos, list friends, and so forth
PATCH/PUT Update a customer, replace goods in an order, switch plane seat, edit an order’s delivery method, change an order’s currency, modify a debit card limit, temporarily block a credit card, and so on
DELETE Delete a customer, cancel an order, close a case, terminate a process, stop a timer, and so on

If you really cannot find a resource and HTTP method pair to represent your action, you can use the default POST HTTP method as a last resort. We will talk more about this in section 3.4.

3.2.5 REST API and HTTP cheat sheet

Congratulations! You have learned to transpose API goals into REST resources and actions, and represent them using the HTTP protocol. You should now have a good overall view of REST API resources and actions. Let’s sum up everything you’ve learned thus far with a cheat sheet, shown in figure 3.21. That makes a lot of things easier to remember!

Remember, at the beginning of this chapter, you saw that the result of GET /products/P123 was some data. We now have to design that data!

03-22.png

Figure 3.21 REST API and HTTP cheat sheet

3.3 Designing the API’s data

You now know how to transpose API goals into REST resources and actions and give them a programmable representation with paths and methods using the HTTP protocol. You have also identified the actions' parameters and returns. But the resources, parameters, and returns you have identified are only vaguely described. How do we design these data items? The design process is outlined in figure 3.22.

03-23.png

Figure 3.22 Designing API data

Whatever the type of API, we start designing the data just like any programmable representation of a concept—just like a database table, a structure, or an object. We simply list the properties and stay consumer-oriented. Consumers must be able to understand the data, and we must not expose inner workings through its design. Once we’ve designed the core concepts, we can design the parameters and responses by adapting them. And finally, we must ensure that consumers will be able to provide all the data required to use the API.

As before, the simplest method shown here is intended to expose the basic concepts. Feel free to adapt it or use a different software design method that you’re familiar with, as long as you keep the spirit alive and achieve the same results.

3.3.1 Designing concepts

The concepts that we have identified and turned into REST resources will be exchanged through parameters and responses between the consumer and provider. Whatever its purpose, we must take care in the design of such a data structure to offer a consumer-oriented API, just as we did when designing the API goals. Figure 3.23 illustrates how to design a concept such as a product.

03-24.png

Figure 3.23 Designing a consumer-oriented concept

We start by listing the data structure’s properties and giving each a name. A product can have reference, name, and price properties, for example. It could also be useful to tell the customer when the product was added to the catalog (dateAdded) and let them know whether it’s in stock or not (unavailable). And what about listing the warehouses where this product can be found and its suppliers? Finally, we might want to return a fuller description of the product.

While listing these properties, you must remember what you learned in chapter 2 about the consumer’s and provider’s perspectives. We must analyze each one to ensure that our design is focused on the consumer’s perspective and does not reflect the provider’s. We can do this by asking ourselves if each property can be understood, is really the consumer’s business, and is actually useful, as shown in figure 3.23. In our example, the property names seem understandable; we have avoided obviously cryptic names such as r and p for reference and price, for example. But on second thought, the warehouses list isn’t really relevant for users, so we’ll remove that. We’ll also rename the unavailable property to definitelyOutOfStock to be more explicit.

The most important information about a property is its name. The more self-explanatory the name is, the better. But defining a property only by its name isn’t enough when it comes to describing a programming interface, as shown in figure 3.24.

03-25.png

Figure 3.24 Property characteristics

We also need to be clear about each property’s type. For example, what is the type of the reference property? Is it a number or a string? In this use case, it’s a string. Is it an essential property that must always requested or returned? That is the case for this one. And a final question: What exactly is a reference? Its description indicates that it is a unique ID identifying the product.

As figure 3.24 illustrates, for each property, the characteristics we need to gather are

  • Its name
  • Its type
  • If it’s required
  • An optional description when necessary

Figure 3.25 shows a detailed list of the possible properties of the product resource. It also shows on the right of the properties list an example of a product’s JSON document.

03-26.png

Figure 3.25 The product’s resource properties

So, a product is composed of required and optional properties. Some of them (such as reference, price, definitelyOutOfStock, and dateAdded) are of basic types (such as string, number, boolean, or date). There can also be complex properties, such as supplier, which is an object. (A property can also be an object containing properties or an array.)

A name and a type are the most obvious information to gather about a property when designing a programming interface. An API can be consumed by software written in many different languages.

In addition to knowing a property’s name and type, we must also know if this property should always be present. Indicating whether a property is required or optional is an often-forgotten aspect of API design, but this information is crucial in both the parameter and response contexts for API designers, consumers, and implementers. Note that the required or optional status of a property can vary depending on the context. For now, we’ll set this status to required only if it is an essential property of the concept.

Sometimes, name and type are not sufficient to accurately describe a property. To provide additional information that cannot be obviously reflected by the property’s name and type, adding a description can be valuable in such cases. Once we know what our concepts are made of, we can use them as responses or parameters to our goals.

3.3.2 Designing responses from concepts

The same concept can appear in different contexts, as shown in figure 3.26.

03-27.png

Figure 3.26 Different representations of the same concept in different response contexts

For example, the catalog resource actions add product and search for products both return a product (or, in the latter case, potentially more than one). The product resource action get product returns a product too. These different product representations might not be exactly the same as shown in figure 3.27.

03-28.png

Figure 3.27 Designing different responses from a single concept

While add product and get product should return the complete product, search for products can only return a reference, name, price, and the supplier’s name as supplierName.

When we design responses, we should not blindly map the manipulated resource. We must adapt them to the context by removing or renaming properties and also adjust the data structure. But do we design the parameters the same way?

3.3.3 Designing parameters from concepts or responses

When we add, update, or replace a product, we pass some product information as a parameter (figure 3.28).

03-29.png

Figure 3.28 Different representations of the same concept in different parameter contexts

But what does this parameter consist of? Or, more precisely, these parameters as shown in figure 3.29. The product information parameter passed in these three cases might not be the same; they may not look like the responses we just designed, and they may not exactly reflect our product concept.

03-30.png

Figure 3.29 Designing different parameters from a single concept

When a product is created, its reference is generated by the backend, so there’s no need for the consumer to provide it when adding a product. But we need this reference to update or replace a product (note that the reference will be passed in the path as a path parameter: /products/{reference}). In all these use cases, the consumer does not need to provide the supplier.name property; only its reference is needed; the backend has a way to find a supplier’s name based on its reference. To simplify the data organization, we can therefore remove the supplier object and replace it with supplierReference. dateAdded is also generated by the backend when the product is added to the catalog, so we don’t need that either. As with responses, the same concept can have different representations in an API’s parameters, depending on the context (creation versus updating, for example).

By proceeding this way, we ensure that the product information parameter contains only the data that’s absolutely necessary in each context. But are we sure the consumer can provide all this data?

3.3.4 Checking parameter data sources

When adding a product to the catalog, consumers should be able to easily provide data such as name, price, and description. But what about the supplierReference? How can consumers know such a reference? This kind of questioning probably sounds familiar. That’s because when identifying the API goals, we verified that consumers could provide all the necessary inputs, either because they already know the information or because they are able to retrieve it from another API goal. But now we are dealing with a more detailed view of these input parameters.

Consumers must be able to provide all of a parameter’s data, either because they know the information themselves or because they can retrieve it from the API. If data cannot be provided, it might be a sign of a missing goal or the provider’s perspective. Therefore, we must verify again that all the needed data can be provided by the consumer. This verification process, illustrated in figure 3.30, ensures that consumers will always be able to provide parameter data, and that there are no gaps in the API.

03-31.png

Figure 3.30 Verifying that consumers can provide all the parameter’s data

In this case, consumers might already know the supplierReference because it is provided on the product’s label, or it may come from a response we have already designed. It could also come from another goal; in which case, we just need to update its response to add this information in order to provide it to the consumer. We may also simply have missed a goal. In that case, we will have to add it to the API goals canvas and process it like any other goal, identifying who can use it, defining its inputs and outputs, and designing its programmable representation. Or we might simply realize that we don’t really need it.

We will not solve this mystery here—its purpose is to show that parameters must be closely verified and that you can discover some missing features when you get into the details of an API’s design. This is totally normal; designing an API is a process of continuing improvement. Step-by-step, the design will be refined to become more accurate, complete, and efficient.

3.3.5 Designing other parameters

What about the free-query parameter of the search for products goal? It is a string that can contain a partial description, a name, or a reference. This is an optional query parameter; if it is not provided, all available products are returned.

Whatever the parameter is—a query parameter like free-query or even a body parameter not based on an identified concept—we do the same thing. We choose a consumer-oriented representation and check that the consumer is able to provide any requested data.

With what you’ve learned so far, you should be able to design any basic REST API. But sometimes you might encounter challenging design problems and sticking to the chosen API type representation might be hard. Perfection is not always possible in the API design world.

3.4 Striking a balance when facing design challenges

When you choose to use a specific API type, it is important to know that sometimes you can encounter limitations. You might struggle to find a representation of a goal that conforms to the chosen API model. You might also end up with a representation that conforms to the model but is not as user-friendly as you expected. Sometimes the perfect representation does not exist, and therefore, as an API designer, you will have to make some trade-offs.

3.4.1 REST trade-off examples

Mapping actions on resources to HTTP methods and paths is not always straightforward. There are common techniques to circumvent such problems, and often by exploring various solutions, you can finally find one that works with your chosen API model. But sometimes this API style-compliant solution might not be user-friendly.

You might have noticed that I carefully avoided transposing goals that were related to a user buying products and focused on the catalog-related goals. Goals related to catalog management are perfect to show the basics of HTTP and REST APIs. The resources are relatively simple to identify, and goals with actions such as add, get or search, update, and delete (who said CRUD?) are easily mapped to HTTP methods. But things aren’t always so simple.

When users buy products, they have the check out cart goal at the end of the process. How can we represent such a goal when it’s not obvious how to transpose it into a path and HTTP method couple? When a designer fails to map an action on a REST resource to any HTTP method, a first option is often to create an action resource (figure 3.31).

03-32.png

Figure 3.31 Using an action resource

An action resource is not a thing represented by a noun, but an action represented by a verb. It is simply a function. An action resource can be seen as a method of a class and, therefore, can be represented as a sub-resource of a resource. If we choose to represent the cart resource by the /cart path, the cart.checkout() method could be represented by the path /cart/check-out. But we could also consider it as a standalone checkoutCart() function and create a /check-out-cart action resource accordingly. In both cases, we use the HTTP method POST to trigger the action resource.

An action resource absolutely does not conform to the REST model, but it works and is totally understandable by consumers. Let’s see if we can find a solution that’s closer to the REST model. We could, for example, consider that checking out the cart changes some status (figure 3.32).

03-33.png

Figure 3.32 Updating a status

The cart resource might contain a status property. To check out the cart, we can update it with PATCH to set its value to CHECKING_OUT. This solution is closer to the REST model but less user-friendly than the action resource: the check out cart goal is hidden within an update of the cart resource. If we keep on brainstorming, I’m sure we can find a solution that totally fits the REST API model.

Let’s get back to the basics. We must ask ourselves a few questions:

  • What are we trying to represent?
  • What happens in this use case?
  • What happens when we check out a cart?

Well, an order is created that contains all the cart’s products. And after that the cart is emptied. That’s it! We are creating an order. Therefore, we can use a POST /orders request to create an order and check out cart, as shown in figure 3.33.

03-34.png

Figure 3.33 Conforming to the REST API model

This solution totally conforms to the REST API model, but is it really user-friendly? The purpose of this REST representation might not be obvious to all users.

3.4.2 Balancing user-friendliness and compliance

So which option wins? The totally non-REST but so user-friendly POST /cart/check-out or POST /check-out-cart action resources? The more REST but a little bit awkward PATCH /cart? Or the totally REST but not so user-friendly POST /orders? It will be up to you to choose (figure 3.34) or to find a better solution.

03-35.png

Figure 3.34 A balance between user-friendliness and API type compliance

Sometimes you might not find the perfect API goal representation, even after intense brainstorming and with the help of the entire team. Sometimes you might not be really satisfied or even be a bit disappointed by an API design you are working on. Unfortunately, this is totally normal.

It is important to master the fundamentals behind the chosen programming interface model or API style to be able to find solutions that are as close as possible to the chosen model. But it is also important to be able to make some reasonable trade-offs to keep the API consumer-friendly and not diverge too much from the API model. Skill at this comes through practicing, observing other APIs, and, most importantly, talking to your consumers and other API designers.

Congratulations! You should now be able to transpose any API goal to a REST API. But now that we have covered what a REST API is and how to create one based on an API goals canvas, we should explore REST beyond mapping goals to HTTP method and path pairs. This is important because REST matters for the design of any type of API.

3.5 Understanding why REST matters for the design of any API

As T.S. Eliot said, “The journey, Not the destination matters….” I could have explained all the API design principles presented in this book using the totally outdated Xerox Courier RPC model that was created in the 1970s, the despised SOAP model created at the end of the 20th century, the now widely adopted REST, or the more recent gRPC or GraphQL. And these are only a few examples among many. As explained in chapter 1 (section 1.3), there have been, there are now, and there always will be different styles of programming interfaces allowing software to communicate remotely. Each of them had, has, or will have its own specificities, pros, and cons, and will obviously produce an API with a specific look and feel. But whatever its type, designing an API requires basically the same mindset.

Up to this point, we’ve been considering REST APIs as APIs that map goals to paths and HTTP methods. But REST is far more than that. REST is a widely adopted API style; but, more importantly, it is based on the solid foundations of the REST architectural style, which is crucial to know when creating any type of API. That’s why I chose REST as the main example programming interface for this book. Let’s see what this REST style is, and what it means not only for API designers but also for API providers.

3.5.1 Introducing the REST architectural style

When you type a URL such as http://apihandyman.io/about in a web browser’s address bar, it sends a GET /about request to the apihandyman.io web server. It’s easy to imagine that the web server will return some static HTML document stored in its filesystem, but that might not be the case. The /about resource’s content could be stored in a database. And what happens when a social media web server receives a POST /photos request? Does the server actually store the provided file as a document in a /photos location on the server’s filesystem? Maybe. Maybe not. It could also store this image in a database.

Browsers interacting with web servers are left totally unaware of such implementation details. They only see the HTTP interface, which is only an abstraction of what it can do and not an indication of how it is done by the server. And how is it that a web browser can interact with any web server implementing an HTTP interface? It’s because all web servers use exactly the same interface.

This is a part of the magic of HTTP. This is a part of the magic of REST.

The REST architectural style was introduced by Roy Fielding in 2000 in his PhD dissertation “Architectural Styles and the Design of Network-based Software Architectures.” Fielding developed this architectural style while he was working on version 1.1 of the HTTP protocol. During the HTTP 1.1 standardization process, he had to explain everything—from abstract web notions to HTTP syntax details—to hundreds of developers. That work led to the creation of the REST model.

The aim of the REST architectural style is to facilitate building distributed systems that are efficient, scalable, and reliable. A distributed system is composed of pieces of software located on different computers that work together and communicate through a network, like the one shown in figure 3.35.

03-36.png

Figure 3.35 A distributed system

This should sound familiar to you because, from the beginning of this book, we have talked about distributed systems. A web browser and a web server comprise such a system, as do a consumer (like a mobile application) and API servers. Such systems must provide for fast network communication and request processing (efficiency), be capable of handling more and more requests (scalability), and be resistant to failure (reliability). The REST architectural style also aims to facilitate portability of components (reusability), simplicity, and modifiability. To achieve all this—to be RESTful—a software architecture needs to conform to the six following constraints:

  • Client/server separation —There must be a clear separation of concerns when components like a mobile application and its API server work and communicate together.
  • Statelessness —All information needed to execute a request is contained within the request itself. No client context is stored on the server in a session between requests.
  • Cacheability —A response to a request must indicate if it can be stored (so a client can reuse it instead of making the same call again), and for how long.
  • Layered system —When a client interacts with a server, it is only aware of the server and not of the infrastructure that hides behind it. The client only sees one layer of the system.
  • Code on demand —A server can transfer executable code to the client (JavaScript, for example). This constraint is optional.
  • Uniform interface —All interactions must be guided by the concept of identified resources that are manipulated through representations of resource states and standard methods. Interactions must also provide all metadata required to understand the representations and know what can be done with those resources. This is the most fundamental constraint of REST, and it is the origin of the Representational State Transfer name. Indeed, using a REST interface consists of transferring representations of a resource’s states.

That might sound terribly scary and far from API design concerns, but these constraints should be understood by any API provider, in general, and any API designer, in particular.

3.5.2 The impact of REST constraints on API design

The REST architectural style was primarily created as a support to describe the World Wide Web and the HTTP protocol, but it can be applied to any other software architecture design with the same needs. A REST API, or RESTful API, is an API (which, in a broad sense, comprises both the interface and its implementation) that conforms (or at least tries to conform) to the REST architectural style constraints. These constraints obviously have a lot of implications for REST APIs but also for any type of API. Some of them can be a little hard to grasp at this time, but we will explore them throughout the book while digging into the various aspects of API design. What if I were to tell you that we’ve already started to explore three of them, perhaps without you realizing it?

Do you remember the consumer’s perspective we talked about in the previous chapter? As shown in figure 3.36, there are two REST constraints underneath this design principle.

When exploring the consumer’s perspective, we saw that an API provider must not delegate its job to the API consumer—like turning the magneton on and off on the Kitchen Radar 3000 (see section 2.1). This is an example of the client/server constraint.

03-37.png

Figure 3.36 REST constraints and the consumer perspective

We also saw that an API consumer is only aware of the provider’s API and does not know what’s happening beyond this interface—like in the restaurant example where customers order meals without having a clue about what is really happening in the kitchen (see section 1.2.2). This is what the layered system constraint means. These two constraints, and focusing on the consumer’s perspective in general, will help you build APIs that are easy to understand, use, reuse, and evolve.

We also have started to uncover the uniform interface constraint in section 3.2.3 and section 3.2.4 as illustrated by figure 3.37.

03-38.png

Figure 3.37 Creating APIs with uniform interfaces using HTTP

Each resource is identified by a unique path. Inside a single API and across APIs, POST /resource-A and POST /resource-B have the same meaning: create something. By representing goals with HTTP using unique paths and, most importantly, standardized HTTP methods, we are creating a uniform interface, which is consistent with itself and also with other interfaces. In chapter 6, we will get deeper into the other aspects of the uniform interface. We will talk more about REST representation and discover the other constraints (statelessness, cacheability, and code on demand, for example) while learning to design APIs in the next chapters. Table 3.2 gives a recap of all sections describing the REST architectural style constraints.

Table 3.2 REST constraints in The Design of Web APIs

REST constraint Insights in the book
Client/server separation constraint Chapter 2, section 2.1
Statelessness constraint Chapter 5, section 5.3.4
Cacheability constraint Chapter 10, section 10.2.2
Layered system constraint Chapter 1, section 1.2.2
Code on demand constraint Chapter 5, section 5.3.2
Uniform interface constraint This chapter, section 3.2.3 and section 3.2.4, and chapter 6

In the next chapter, you will discover a structured way of describing programming interfaces, much like the one we have designed, by discovering why and how to describe an API using an API description format.

Summary

  • A REST API represents its goals with actions (HTTP methods) on resources (paths).
  • You must use portable data such as object, array, string, number, date, or boolean types when designing data.
  • A single API concept can have multiple data representations in different contexts.
  • If a parameter contains data that cannot be provided by consumers, you missed something.
  • Sometimes you will be frustrated or disappointed when designing APIs and having to strike a balance while facing design challenges—this is totally normal.
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset