Chapter 4. Web services explained

This chapter covers

  • HTTP fundamentals
  • REST web services
  • HTTP traffic
  • Web API best practices
  • Troubleshooting

Now that you’re familiar with what APIs do and have some overall idea about the strategy, let’s dive deeper into the technical aspects of HTTP and REST so that you understand how these systems work together. Knowing these details will also help you when you want to troubleshoot or understand how the interactions are working between a client and server.

Note that in order to grasp how HTTP matters in the API context, you need to know the protocol and practices from the point of view of the consumers—your developer partners. So while you’re working to make a great API, understanding the interactions between client-server transactions from the point of view of a developer who is consuming the service is illuminating. This chapter is designed to give you a view of the user side of HTTP so that you can determine how the pieces fit together in an API. This chapter will provide an understanding of how APIs work, which is necessary to build APIs people love.

4.1. HTTP fundamentals

An API can be any kind of transaction between two computer systems, but this chapter focuses on REST APIs specifically. Although REST APIs don’t have to use HTTP, this common protocol has all of the fundamental pieces needed to build a REST API, and so almost all REST APIs use HTTP as the transaction protocol (the layer underlying the REST API). First, we’ll drill down on HTTP more deeply than we did in chapter 2, and then we’ll explore the specifics of how the interactions work.

As mentioned in chapter 2, HTTP is the method browsers use to communicate with web servers. When you click a web link or type an address in a URL bar, your browser sends an HTTP request to the server, and the server sends back an HTTP response (see figure 4.1).

Figure 4.1. HTTP transaction between a client (application) and server (platform). The HTTP request will always be sent to a specific URL defining the resource; most requests will also have headers to add context to the request, along with parameters to refine the item itself. If the request is an update or create action, a body will be sent as well. The HTTP response always has a status code indicating the success or failure of the exchange, as well as headers describing the type and size of the content.

Several concepts are inherent in the HTTP protocol:

  • Addressability
  • Status codes
  • Body
  • HTTP verbs
  • Headers
  • Parameters

4.1.1. Addressability

One requirement for any system is a way to express exactly what you want to operate on. When you enter a web address into the URL bar of your browser, you’re indicating the unique identifier for a specific item—in this case, a page on the internet. Every HTTP request must have an identifier; this address is called a uniform resource indicator (URI) in this case. Table 4.1 shows examples of addresses. In each address, there’s a server component and a path component.

Table 4.1. URLs broken out by server and path

URL

Server

Path

http://www.google.com/ www.google.com / < This is the base of the website, the top page.
http://gmail.com/ gmail.com /
http://irresistibleapis.com/demo/ irresistibleapis.com /demo/

The unique identifier of a resource (or page) is the combination of the server name and the path. This format ensures that each request addresses a specific item on the internet.

4.1.2. Status codes

As every request must have an address, every response comes back from the server with a status code indicating the success or failure of the action requested. The HTTP status code system is fairly straightforward. The simplest explanations of the groups are shown in table 4.2.

Table 4.2. HTTP status code groups

Status code group

General description

5XX We (the server) messed up
4XX You (the client) messed up
3XX Ask that guy over there
2XX Cool!

Each status code group represented in table 4.3 expresses a broad meaning, and examples are given of what types of errors you might encounter in that group.

Table 4.3. HTTP status codes and specific examples

Status code

Meaning

Examples

5XX Server error 500 Server Error
4XX User error 401 Authentication failure 403 Authorization failure 404 Resource not found
3XX Redirect 301 Resource moved permanently 302 Resource moved temporarily
2XX Success 200 OK 201 Created 203 Object marked for deletion

5XX errors (status codes between 500 and 599) mean that the server itself encountered an error. When writing your API, you must give good, strong error messages with the appropriate status codes. If developers encounter server errors intermittently on a particular resource when developing a client for an API, they may be able to retry the request, but you’ll want to watch and log the number of errors and other status codes in your system. Otherwise, this is likely an error the API provider (in this case, you) will need to fix in the system. Table 4.4 describes the specific client error codes used in HTTP transactions.

Table 4.4. HTTP 4XX status codes

Status code

Meaning

Description

400 Malformed request Frequently a problem with parameter formatting or missing headers.
401 Authentication error The system doesn’t know who the request is from. Authentication is like a driver’s license, showing who you are. Authentication signature errors or invalid credentials can cause this.
403 Authorization error The system knows who you are but you don’t have permission for the action you’re requesting.
404 Page not found The resource doesn’t exist.
405 Method not allowed Frequently a PUT when it needs a POST, or vice versa. Check the documentation carefully for the correct HTTP method.

Authorization is the next step. Once the system knows who you are, it can determine what you’re allowed to see and do in the system. As indicated in table 4.4, the 403 status code indicates that the system has determined that the user doesn’t have permission to execute the specified request. Again, when returning a 403 status code, there should be an additional message describing the problem.

Almost everyone is familiar with the 404 status code, the code that’s returned when a particular page doesn’t exist. In addition to the well-known client error status codes listed in table 4.4, a huge number of 4XX errors are out there—for missing or unexpected parameters, headers, or incorrect data formatting. There’s even a 418 status code put in by the W3C Standards Body for an April Fool’s joke in 1998 (see figure 4.2); the W3C wrote a coffeepot protocol, and 418 was the error “I’m a teapot.” It’s a shame that people say that standards bodies have no sense of humor.

Figure 4.2. 418 status code exchange demonstrating a somewhat silly interaction with the coffeepot protocol as defined by the W3C. Note, though, that it’s similar to the other transactions we’ve seen; the client sends a request for a particular resource, and the platform responds with a status code indicating the answer—in this example, a response that the object isn’t the expected coffeepot, but rather a teapot.

4.1.3. Body

When content is associated with a request or response, this data is sent as the body of the request or response. For requests, this is generally the case for create and update options (POST and PUT). The body gives the server the information it needs to create or update the resource specified by the client. Whether this is JSON text describing the new or updated object or an image file, the body is where the “meat” of the write operation will be placed.

In the case of a read operation (GET), the request doesn’t need to have any data associated with it. In this case, the response will have a body with the content of the resource that was requested by the client.

4.1.4. HTTP verbs

Each individual request from the client to the server needs to indicate the action it wants the server to take. To create a fully functional system, it needs to be able to create, read, update, and delete (CRUD), as shown in figure 4.3.

Figure 4.3. Actions and their associated HTTP methods. To perform all the needed actions for a complete platform, CRUD must be supported. HTTP contains methods for each of these actions, making it a great framework on which to build an API.

The way this works can seem somewhat abstract, so I’ll expand the example that was briefly given in the first chapter. I will order an iced tea from Starbucks, with the requisite additions and modifications I want in order to make it exactly how I want it (figure 4.4).

Figure 4.4. This is a human version of the programmatic transaction that follows. The interaction between the people demonstrates exactly how transactions work. As you can see, all the requests and responses between the client and the server map directly to an easy-to-understand interaction between people.

In the example in table 4.5, the list of orders is at the address /orders, and each individual order is at the address /orders/<number>.

Table 4.5. Iced tea transaction, translated into a set of HTTP transactions

Action

System call

HTTP verb address

Request body

Successful response code Response body

Order iced tea Add order to system POST /orders/ {
"name" : "iced tea",
"size" : "trenta"
}
201 Created Location: /orders/1
Update order Update existing order item PUT /orders/1 {
"name": "iced tea" ,
"size" : "trenta",
"options": [
"extra ice",
"unsweetened"]
}
204 No Content or 200 Success
Check order Read order from system GET /orders/1   200 Success {
"name": "iced tea" ,
"size" : "trenta",
"options": [
"extra ice",

"unsweetened"]
}
Cancel order Delete order from system DELETE /orders/1   202 Item Marked for Deletion or 204 No Content

Going through each of these steps in order will help you understand how the various resources work together to create a story. As I discuss each piece, I’ll describe exactly what each is doing within the system.

First, I place the order with the cashier (table 4.6). “I want a Trenta iced tea.” In HTTP parlance, this would be a create action, indicated with the verb POST. Now, if I were to request this multiple times, the system (in this case, the cashier at Starbucks, using the cash register) would create a new item each time I made the request. If I asked for this 15 times, 15 iced teas could be ordered (imagine that the cashier is as patient and accepting as a computer system might be). POST is the only HTTP verb that should reasonably be expected to create new items. All the other methods should work on already existing (and specifically identified by the URI) elements. The server response in the case of an HTTP POST usually includes the location of the newly created object.

Table 4.6. Place order

Action

System call

HTTP verb address

Request body

Successful response code Response body

Order iced tea Add order to system POST /orders/ {
"name" : "iced tea",
"size" : "trenta"
}
201 Created Location: /orders/1

The next thing I do is specify how I want the iced tea (table 4.7). “Extra ice, unsweetened.” To do this with HTTP you’d use PUT, the verb that’s used to update an existing object in the database. The address used for this action is the one that was retrieved from the POST operation before. The Location information indicates that further operations on this specific order need to use the address returned by the create action. As you can see in table 4.7, the body content for this request includes the entire ordered item, including the information in the originally created order as well as the information you want to add. Sending the update with the body, as shown in table 4.7, will update the system’s version of your order to add the two new options. Note that sending an object that only had your changes (and not the original content) wouldn’t update the object correctly. PUT is a full update on the item; you need to build the entire object and PUT it to the system rather than try to make changes piecemeal. Another HTTP method, PATCH, can be used in this way, but most API servers don’t implement it, and using that kind of system requires a deep understanding of the data model.

Table 4.7. Update order

Action

System call

HTTP verb address

Request body

Successful response code Response body

Update order Update existing order item PUT /orders/1 {
"name": "iced tea" ,
"size" : "trenta",
"options": [
"extra ice",
"unsweetened"]
}
204 No Content or 200 Success

The main difference between PUT and POST is idempotency. This rule says that a PUT transaction must be the same even if you send it to the server 10 times, or 100, or even a million times. Imagine that you’re standing in that Starbucks repeating the phrase “Extra ice, unsweetened” 15 times. Unlike with the earlier POST, you’d be setting options for the same iced tea, and no matter how many times you made the request, the outcome would be the same (although the cashier might look at you somewhat crossly after the first few times). The system itself wouldn’t change anything after the first request, and no extra changes would occur to your iced tea order beyond the first request you send.

That covers create and update. The next two verbs are more straightforward. Back to the Starbucks example, I want to check and make sure the cashier has heard me correctly. “Can you please read that back to me?” The cashier responds, “A Trenta iced tea, extra ice, unsweetened.” In the HTTP world, this is a read operation, represented by the HTTP verb GET (table 4.8). This operation retrieves the server’s current representation of the order in question. This representation matches the version sent by the update operation. No body content is sent with the request because the client isn’t trying to change anything but is retrieving the current object from the server.

Table 4.8. Check order

Action

System call

HTTP verb address

Request body

Successful response code Response body

Check order Read order from system GET /orders/1   200 Success {
"name": "iced tea" ,
"size" : "trenta",
"options": [
"extra ice",

"unsweetened"]
}

Create, read, update . . . that leaves delete (see table 4.9). The HTTP verb for this is DELETE (note that all HTTP verbs are sent as all capitals in request transactions). To finish this transaction, imagine that I’ve left my wallet at home and must cancel the order. For an HTTP client-server transaction, this would be done by sending a DELETE to the item’s unique identifying address. No body content is sent because there’s no new information the server needs in order to perform the deletion. Sending the verb DELETE along with the specific address for the resource tells the server everything it needs to know to perform the requested action. Additionally, there will be no response body. Everything happens without content being passed around.

Table 4.9. Cancel order

Action

System call

HTTP verb address

Request body

Successful response code Response body

Cancel order Delete order from system DELETE /orders/1   202 Item Marked for Deletion or 204 No Content

With POST, GET, PUT, and DELETE, HTTP has verbs available to cover each piece of a full client-server transaction.

Figure 4.5 pulls the address, request information, and status together and demonstrates how the verbs, status codes, and addresses work for specific types of actions. For each of the verbs, an address is used to indicate where to look for the item, or resource, being accessed, whether it’s an object or a list of objects. The verb along with this address combine to indicate what action should be taken with that item. Finally, the Status Code line shows the status code for a successful action.

Figure 4.5. HTTP verbs and their components. Each HTTP request must always have a method along with the resource: the address for the specific item as well as an action to be taken for that item. Note that the Success status code listed for each of the methods is slightly different but still follows the pattern of 2XX, indicating success.

4.1.5. Headers

So far I’ve covered the address for an item, the verb for a request, and the status code in the response. For a simple transaction, this is enough to understand what’s happening. HTTP contains headers, additional elements providing additional context for a request, and parameters, separate options for the request.

There are different ways to send options with a request (headers and parameters), and it’s not obvious which kind of information should go where. Headers are used for context about the entire transaction, as opposed to a single request-response message. In some cases, headers or parameters can both be used for the same kind of information.

To make this clearer, let’s go back to my iced tea example. When I walk into a Starbucks in Barcelona, Spain, the cashier may greet me in English or Spanish. Depending on how I respond, the rest of the conversation will be in the language I choose. In this case, I (representing the client) expressed my preference for English by speaking in English, and the cashier will respond in kind if possible. When you indicate to your browser what your preferred language is, it will send a header along with requests to indicate this preference to the server. If the server is able to provide the response in the requested language, that’s what it should do.

Headers, then, are for context about the entire transaction. Common items passed in a request via headers include language preference, response format preference, authorization information, and compression preference. Table 4.10 shows common headers for requests, with a description of how each one is processed by the server to determine what response to send back.

Table 4.10. Common request headers

Header

Example value

Meaning

Accept Text/html, application/json The client’s preferred format for the response body. Browsers tend to prefer text/html, which is a human-friendly format. Applications using an API are likely to request JSON, which is structured in a machine-parseable way. This can be a list, and if so, the list is parsed in priority order: the first entry is the most desired format, all the way down to the last one (which frequently uses */* to indicate “whatever might be left”).
Accept-Language en-US The preferred written language for the response. This is most often used by browsers indicating the language the user has specified as a preference.
User-Agent Mozilla/5.0 This header tells the server what kind of client is making the request. This is an important header because sometimes responses or JavaScript actions are performed differently for different browsers. This is used less frequently for this purpose by API clients, but it’s a friendly practice to send a consistent user-agent for the server to use when determining how to send the information back.
Content-Length <size of the content body> When sending a PUT or POST, this can be sent so the server can verify that the request body wasn’t truncated on the way to the server.
Content-Type application/json When a content body is sent, the client can indicate to the server what the format is for that content in order to help the server respond to the request correctly.

The response also includes headers, which generally specify the content type (the formatting used for the response), the language used, or the size of the request. Table 4.11 gives examples of common response headers along with what they indicate to the client.

Table 4.11. Common response headers

Header

Example value

Meaning

Content-Type application/json As with the request, when the content body is sent back to the client, the Content-Type is generally set to help the client know how best to process the request. Note that this is tied somewhat indirectly to the Accept header sent by the client. The server will generally do its best to send the first type of content from the list sent by the client but may not always provide the first choice.
Access-Control-Allow Headers Content-Type, Authorization, Accept This restricts the headers that a client can use for the request to a particular resource.
Access-Control-Allow-Methods GET, PUT, POST, DELETE, OPTIONS What HTTP methods are allowed for this resource?
Access-Control-Allow-Origin * or http://www.example.com This restricts the locations that can refer requests to the resource.

4.1.6. Parameters

Parameters are frequently used in HTTP requests to filter responses or give additional information about the request. They’re used most frequently with GET (read) operations to specify exactly what’s wanted from the server. Parameters are added to the address. They’re separated from the address with a question mark (?), and each key-value pair is separated by an equals sign (=); pairs are separated from each other using the ampersand (&).

Table 4.12 demonstrates different ways the request could have been made to get information about the order in question using filters and other specifiers.

Table 4.12. Using parameters to specify details of read requests

Action

System call

HTTP verb address

Successful response code Response body

Get order list, only Trenta iced teas Retrieve list with a filter
GET
/orders?name=iced%20tea&size=trenta
[
 {
   "id" : 1,
   "name": "iced tea" ,
   "size" : "trenta",
   "options": [
           "extra ice",
           "unsweetened"]
 }
]
Get options and size for the order Retrieve order with a filter specifying which pieces to return
GET
/orders/1?fields=options,size
{
   "size" : "trenta",
   "options": [
           "extra ice",
           "unsweetened"]
 }

These two examples are somewhat different ways of understanding how parameters can be used in a request. The first example demonstrates the use of parameters to filter which items are retrieved from a list. As described earlier, the /orders/ resource is a list of all of the orders in the system. So a GET call to /orders/ would return all orders currently within the system. In this case, I’m only interested in iced tea orders, so I add a filter on the call to indicate that I only want items with “iced tea” as the name that are size trenta. Note that it’s not necessary for the key names to match the variables within the object itself, but it can be handy for keeping track of what’s being requested.

You probably noticed that this request has the question mark to separate out the parameters (sometimes called a query string) from the resource address, and that the two key-value pairs are separated from each other using the & character. You may have also noticed that iced tea became iced%20tea. Certain characters such as the space character need to be escaped in this way in order for a web server to process them correctly. In most cases your browser does this for you, but when a developer creates a client, it’s important to remember to run the requests through an encoder to make sure that the server gets the right information.

The return value on this initial request also shows a difference with the original GET to the specific order. Instead of returning an object (indicated by key-value pairs encased in curly braces {}), this request returns an array (indicated by a list encased in brackets []). That’s because you’re requesting a list, and the server is returning a list with one item in it. A list response should always be in the format of a list, even if there’s only one item within that list. This consistency makes it easier for developers to work with the responses whether there are zero, one, or many elements in the response.

The second example shows how to ask for specific information about a single known item. The request is to the same single order used before, but in this case specific information about that order is being requested. Neither this nor the previous example will “magically” work, but it’s a great way for an API/web service provider to provide developers with more ways to get exactly what they need from the system without getting extra, unneeded information. Trimming the response in this way is often useful for clients like mobile applications where the bandwidth is quite restricted and the application needs to get information as quickly as possible. In this case, the client is asking for the specific order numbered “1,” but only the size and options for that order. The response is smaller than the full order in the original example and has exactly what the client wants for their purpose.

4.1.7. HTTP overview summary

This overview of HTTP isn’t complete or exhaustive but should give you enough information to move forward with the rest of the concepts for web APIs. If you’re interested in more detailed information about HTTP and how it works, you can find many resources on the web and in print.

4.2. REST web services explained

Now that you’ve got an idea how HTTP works, it should be much easier to understand the mechanics of the API you worked with in chapter 2. REST-style APIs (and many other similar web APIs) are effective when designed and built around HTTP. The HTTP verbs match exactly to the create, read, update, and delete (CRUD) actions needed. The existing error framework is more than sufficient for a strong framework.

There are some best practices for REST web services built on HTTP. For instance, the URI (web address) for a resource must be unique for each item. This is the item’s unique identifier and is used for any action on that item. Because only POST transactions should add new items to the system, POST is generally used for create operations in REST by operating directly on a list resource rather than a single object. When you create a new object in the system, you send a POST to a list of items. If you look back at the pizza toppings example, you’ll see that when new objects were created, that was done with a POST to the list of toppings, whereas update actions were done using PUT to the specific topping.

REST seems like a difficult and abstract way to describe a system, but if you take the HTTP fundamentals described and use them in a direct way, you’re most of the way to creating a good REST API. As illustrated in the Starbucks example, the HTTP protocol supports all the needed methods for a complete transaction set. REST utilizes these methods to create interactions that are straightforward, consistent, and predictable.

As I discuss pitfalls and best practices, you’ll learn to avoid the most common mistakes made when people are implementing REST-style APIs.

4.3. Exploring your API by inspecting HTTP traffic

In chapter 2, you were introduced to some ways to inspect HTTP traffic. Understanding how your developers will interact with your system is a critical component of API success, so you need to test your system as if you were one of the developers trying to use it. Inspecting API traffic is also helpful when creating support documentation or helping developers. Watching traffic using a sniffer is quite useful when trying to debug complex API calls, but for the purposes of understanding how HTTP traffic works in general, using a browser is a fine choice. For this discussion, you’ll put together the information in section 3.1 with web calls. I suggest using Chrome or a similar browser with HTTP traffic inspection. To start, I discuss how to get Chrome set up correctly to give you the information you need, and then we’ll explore how it applies more completely to the example application discussed in chapter 2.

4.3.1. Setting up Chrome for HTTP inspection

To set up Chrome for HTTP inspection, you first need to enable the Developer Tools. If you don’t have one already, get a copy of Chrome to use on your system. One major advantage to Chrome in this case is that it’s available for all major operating systems and works consistently across each of them. To download Chrome, go to www.google.com/chrome/ and get the appropriate version for your system.

After you’ve got Chrome up and running, the first thing to do is go to my website: www.irresistibleapis.com/demo. Figure 4.6 shows what it looks like in Chrome.

Figure 4.6. Go to irresistibleapis.com/demo to see the main HTML page. Remember that on the back end, this page is calling the /toppings endpoint in the API.

Now that you have the basic tool up and running, you need to configure it to give you the information needed in order to inspect the traffic it’s processing. Find the Developer Tools in the menu, as shown in figure 4.7.

Figure 4.7. Once you’ve started the website in Chrome, you can activate the C Developer Tools using the path View > Developer > Developer Tools.

Once you’ve opened the Developer Tools, you need to tell Chrome that you want to be able to see network traffic (this parameter adds overhead to the requests, so it’s disabled by default). To do that, click the Network button in the Developer Tools at the bottom of the page. Figure 4.8 shows what this will look like in your browser.

Figure 4.8. Now that you’ve opened the Developer Tools, switch to the Network tab to get to the information you want to see.

Once on the Network tab, you need to check Preserve Log. Normally, Chrome throws away old network traffic as it goes in order to keep the memory footprint low, but in this case, that’s the information you want. Figure 4.9 shows where this checkbox is.

Figure 4.9. Change the settings to activate Preserve Log so that the HTTP traffic will stay in the queue for inspection.

Now Chrome is set up to show you what you want to see about the website you’re browsing. The log in the Developer Tools is particularly useful, because you can see both the website page and the API interactions (because they’re calls sent directly by the browser itself). Note that in most cases you won’t be able to use a browser to debug or inspect traffic to an API because most APIs require complex authentication, but because this API doesn’t have any authentication and is open to browsers, it’s an excellent learning tool for this situation.

Reload the page you were on (www.irresistibleapis.com/demo or your local API/app server if you set one up in chapter 2). The Developer Tools section should now show you all the resources you’re accessing and give you the opportunity to inspect them individually. Reload the page so that the elements will show up in the Developer Tools section. I’ll start with the most basic piece—you’ve seen it before—and then I’ll work up to the more complex pieces in the stack. All the elements used to build the page, such as the base page, the CSS, and any JavaScript, are displayed in the list. The first thing you want to view is the toppings call, the direct call to the back-end API. This will cause the network log to display both the front-end page and the back-end REST API calls. Figure 4.10 shows how to select this call from the Developer Tools.

Figure 4.10. As calls are made through the browser, they each show up in a separate line in the Network Log section of the Chrome Developer Tools. To get to where you can understand the interactions, select the toppings resource in the network log.

Initially, the Network tab will likely show you the headers, as mentioned earlier. Check out the headers and compare them to the table of request and response headers so you can understand how the headers interact in this live call. This is the traffic that’s flowing into and through the browser. Click the Response tab to see what the response content body looks like, as shown in figure 4.11.

Figure 4.11. Once the toppings resource is selected, you’ll be able to see information about the call in the right-hand column, including the response body, request, and response headers. Explore the various tabs on the right side to learn more about how the pieces work together.

What you see is that the main page at http://irresistibleapis.com/demo is making a back-end call to the API displayed in chapter 2. You can see here that as part of building this page, the HTML page is pulling information from the resource for the list of toppings, in this case /api/v1.0/toppings, as you saw in chapter 2. So now, the JSON being returned from the back-end API is being used to build the HTML page so that you can interact with the individual actions: CREATE (Add new topping), GET (View topping), DELETE (Delete), UPDATE (View, and then change). This application directly interacts with the API described in chapter 2. It’s no coincidence that the JSON being returned matches exactly what the web page shows. Try adding and deleting items in the system and watch how the JSON from the API changes; the page itself isn’t making the changes—it’s making the calls directly to the back-end system.

4.4. Web services best practices

When developing a web API, then, it’s important to follow guidelines that keep your platform in line with the HTTP standard, and with the guidelines set forth in the REST philosophy discussed in section 4.2. Your resources should be nouns, not verbs (so /orders/ instead of /create_order). REST best practices dictate that you should conform to the HTTP status and error codes and work hard to achieve and maintain consistency, making your API as usable as possible for the developers using your system.

Use cases are critically important even at the beginning of your design process, so think carefully about them. For instance, if you want to support mobile applications (ever) with your platform, you need to realize that mobile developers have different issues than standard web developers. The bandwidth is smaller, and mobile devices are unreliable in terms of keeping their connection up (the user can ride a train into a tunnel or enter an elevator, and the network connection can be dropped). You need to provide these people with a single call per page so that their applications can be performant and successful.

This may seem out of line with the idea of single nouns for resources as suggested by the REST philosophy, but realistically what you need is the system that will work best for the developers trying to use your system. Try to make your system as similar to the industry standards as you can, but when those standards clash with use cases critical to your developers, you need to flex in the right direction so the things you want them to do are easy and straightforward (and reliable, and scalable).

4.4.1. Using the right status codes

As discussed previously, HTTP status codes are quite flexible and able to handle most issues encountered during a client-server transaction: server errors (5XX), client errors (4XX), redirects (3XX), and successful transactions (2XX). Think quite carefully about how you plan to return information to the client indicating that an issue has happened. When returning 4XX and 5XX error codes, you can still return a body with a message indicating how the transaction failed. Was it a bad signature or a mismatched parameter, or was the request too large? Ideally you should make your system so talkative that anyone using it can tell immediately what’s happened to make the request fail. Without this, there’s an enormous support burden; any time client developers run into an error, they can’t figure out what the underlying problem is, even though in most cases the problem is something they could fix if they understood the issue. Give your developers the chance to succeed without having to tap your support resources. They’ll be happier, and you’ll spend less time and money trying to handhold your customers through an awkward interface.

4.4.2. Methods and idempotency

Earlier I touched on the notion of idempotency, the idea that when sending a GET, DELETE, or PUT to the system, the effect should be the same whether the command is sent one or many times. When working with web developers, you should follow certain expectations about HTTP method behaviors. For instance, developers who are accustomed to working with well-designed web APIs may get confused if they find an API where a GET (read) command can be used to delete items in the database. This restriction was initially designed to avoid having web crawlers accidentally delete all the items in your system, but even now, with authentication in front of most systems, developers use the type of call into the system as an indication of whether the call itself can be destructive or change the state of the server. Follow the guidelines—a GET is for reading only and shouldn’t be extended to perform other operations.

There are situations where the exact correct method can’t be used consistently. For instance, Flash was originally unable to handle anything other than POST and GET, and as a result it was necessary to overload those two methods so that they could support faux DELETE and PUT operations. Many API vendors found ways to work around this, most of them settling on ways to extend POST to perform other write operations (using headers to indicate the desired action).

In any case, as much as possible you want to follow the existing patterns out in the API ecosystem. The less you can surprise your developers, and the fewer times they have to learn a new pattern for your API, the more likely they’ll be to use the system the way you expect them to. This leads to happier developers, better street cred, and most important, a much smaller support load.

4.4.3. Nouns vs. verbs

As with the methods discussed earlier, it’s critically important to use nouns for objects in a web service system. Although SOAP APIs used methods, we’re focusing on REST-style APIs. In this case, we’re not using a GET to /create_order; we’re using a POST to /orders. The format of this call communicates to the developer that this is a system-changing call and that they’re adding something new to the system. If a crawler did get out of hand with its GET calls, there’s no way that it would accidentally change something within the system.

4.5. Troubleshooting web API interactions

One major advantage of a web API over one that’s more tightly coupled is that the HTTP base of the API is easy to trace, making it much more efficient to determine what the transaction looks like—both request and response—and troubleshoot issues that might arise for clients of the platform. There are various ways of examining this traffic to triage issues or speed up development.

4.5.1. Tools for API inspection

Various tools are available for testing API and browser traffic. They’ll show the headers, content, and values for requests and responses. HTTP sniffers like HTTPScoop and Fiddler can be used to determine how the traffic is faring in the journey between your client and server. These tools are covered in more detail in chapter 2, and you should definitely select one to use when working with web APIs, whether you’re creating or consuming them.

4.5.2. Error handling

When you’re developing an API, one of the best ways to make it irresistible is to make your error handling clear, consistent, and easy to follow. Choose the right status codes for the problems your server is encountering so that the client knows what to do, but even more important is to make sure the error messages that are coming back are clear. An authentication error can happen because the wrong keys are used, because the signature is generated incorrectly, or because it’s passed to the server in the wrong way. The more information you can give to developers about how and why the command failed, the more likely they’ll be able to figure out how to solve the problem. This is one case where having sample code that handles the errors can help demonstrate to the developers exactly how to handle specific problems that occur.

4.5.3. Defensive coding

Many developers are accustomed to a world where they own the entire stack of code. When this is the case, assumptions can be made about the stability of particular interfaces, with a strong reliance on unit tests to make sure each component works correctly and on integration tests to make sure that interfaces are in sync. But when the stack is fully owned by a development team, they can avoid strict coding standards where each item used in a system is carefully inspected to make sure it didn’t change.

Although irresistible APIs are not likely to change unexpectedly, sometimes errors or changes do occur. Make sure that your sample code demonstrates how to handle specific status codes. Demonstrate checking that items exist in the object before using them. Write your documentation in a way that shows the best approach to interact with the resources. I’ve seen many cases where a developer using an API made the assumption that a resource would always be identical, and when it changed, their application broke. For web applications, that’s annoying, but with mobile developers it can take weeks to push a new change through the application system for the mobile device, so it’s critically important that they don’t have incorrect expectations.

Encouraging and teaching your partner developers that they need to avoid assuming the system will always be up and running and identical to the day before will lead to a happier world. Web APIs usually have other systems behind them, and the larger the stack, the more possibilities there are for unexpected problems.

4.6. Summary

In this chapter, you’ve been exposed to some of the internals of HTTP, which should help you understand the underpinnings of REST APIs. I’ve shared some of the downsides to rushing through your development and given you some ideas to contemplate in terms of making items accessible and clear to your developers. Topics covered in this chapter include the following:

  • HTTP fundamentals provide a closer look at the formats and sections of requests and responses. Understanding these fundamentals makes it much easier to follow the action in an HTTP sniffer and learn about the structure of the platform as a whole.
  • A deeper explanation of REST fundamentals includes examples of REST APIs for context and demonstrates how you can leverage the HTTP protocol to easily create clean and functional web APIs.
  • Inspecting HTTP traffic gives various ways to look at the traffic between the client and the server to understand the underlying transactions for a system.
  • Web API best practices help you to avoid many of the common pitfalls encountered when creating an API without a well-thought-out plan. Taking the time to explore and understand other APIs will help you to guide and create a fantastic API product.
  • Sometimes technical interactions don’t work as expected, so a discussion of some common error causes and solutions gives you the tools you need to succeed, along with information on preventing these issues in your own web API.

The next chapter walks you through some guiding principles for creating APIs that are useful and successful in the real world. Topics include use cases, compact responses, consistency, social integration, and versioning.

..................Content has been hidden....................

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