5 API Design Tips Beyond REST

Posted in

In recent years, there’s been a massive uptick in web APIs and REST adoption. Since both are trending, more developers claim to be using REST even if they aren’t precisely adhering to Roy Fielding’s original constraints. Developers have also sought to further clarify API design with models such as the Richardson Maturity Model, or The API Security Maturity Model, adding additional layers of compliance to Fielding’s standards.

REST is a set of general architectural constraints. But in real-world settings, API developers have come to rely on additional best practices and expectations. Below, we’ll cover some tips for designing modern web APIs, looking at data format usage, endpoint naming, error codes, pagination, and other tips to supplement REST.

5 Tips For Creating RESTful APIs

Stick With JSON

Technically speaking, a REST API isn’t constrained to working with only one file type. REST APIs can consume XML and SOAP files, for instance. Just because you can doesn’t mean you should, though. As a general rule of thumb, JSON should be the standard format for sending and receiving data with an API. Nearly every web format can use it, for one thing. Most have built-in ways to deal with JSON natively. Nearly every web developer does, as well. That’s reason enough alone to choose JSON over other formats whenever possible.

Other file formats often require additional external processing. XML files often have to be converted to JSON before they can be consumed, for instance. It’s more challenging to manipulate XML data on the client-side, also.

To ensure you’re always working with JSON, you can specify the file type by setting the Content-type variable in the response header for application/json when a request is made. Some apps set the response header to JSON automatically while it must be specified in others. You’ll also want to make sure your API endpoints are returning JSON files.

Let’s take a look at what this looks like in action. This is an example of working with the Express web framework which then parses that request using the body-parser function. The resulting object is then called using res.json.

const express = require('express');
const bodyParser = require('body-parser');

const app = express();

app.use(bodyParser.json());

app.post('/', (req, res) => {
  res.json(req.body);
});

app.listen(3000, () => console.log('server started'));

The bodyParser.json() parses the JSON request into a JavaScript object, which is then attributed to the req.body object.

In the response, set the Content-type header to application/json; charset=utf-8. This method should work with most web frameworks.

Things, Not Actions

Remember, API endpoints should be categorized so that all of the action takes place on the query side. With this in mind, you should name your endpoints after objects and not the things that are happening to them. This could accidentally end up getting interpreted as code by some programs.

Imagine that you wanted to retrieve all of the cars from a vehicle database. Now imagine you return all of those results to the endpoint getCars.

That same naming pattern is used for objects in JavaScript, however. There’s no telling what kind of havoc that such a simple error could wreak.

As a general rule of thumb, name your API endpoints as nouns instead of verbs. If you need to specify or sub-divide further, you can handle that with further subdivisions.

Use Logical Names for Endpoints

One of the main reasons for using APIs is to make interacting with our data more manageable. We lose this advantage if we create too many endpoints to the point where they become difficult to keep track of.
For example, imagine you are maintaining a warehouse of car parts. Now imagine you have an endpoint for every type of item you carry. Before long, you’ve got `/carHeadrests’, ‘truckSeatbeltEnclosures’, ‘carRadialTires’ and so on. It wouldn’t take very long to lose control over the number of endpoints.

Now, consider this alternative. You’ve got one endpoint for each type of vehicle, so /trucks, /cars, and so forth. Then you can have parts for each type of vehicle as a subdirectory: /cars/parts/seatbelts, /trucks/tires/whitewalls, and so on. This helps keep the number of endpoints manageable. It also adheres to an understandable structure that is easy to navigate and use, which helps your API adhere to Roy Fielding’s principle that APIs should be self-descriptive, requiring no external documentation to use them.

Whenever possible, you should try and group relevant data into one endpoint. This is a good idea regardless of how your database might be configured. In fact, it might be a good idea to use a different structure for your API endpoints and your database. This could prevent cybercriminals from getting too much information about your system architecture.

These endpoints can then be interacted with via your regular GET, POST, PUT, and DELETE HTTP commands.

Use Standard Error Codes

Another one of the main reasons that people use APIs is to make data and tools familiar. If you had to learn and memorize a syntax for every tool you use and every site you visit, you’d spend all of your time poring over documentation and help files.

It’s a good idea to stick to standardized HTTP codes, with this in mind. The most common HTTP error codes are:

  • 400 Bad Request: Input fails validation
  • 401 Unauthorized: A user isn’t authorized to access a resource
  • 403 Forbidden: The user is authenticated but not allowed to access a resource
  • 404 Not Found: Resource not found
  • 500 Internal server error: A generic server error
  • 502 Bad Gateway: Indicates an invalid response from another server
  • **503 Service Unavailable – Something unexpected happened on the server-side

Allow Sorting, Filtering, and Pagination

Databases that APIs interact with can quickly become rather large and unwieldy. This means you run the risk of overwhelming your system if you’re not careful about API queries. Filtering is one way to sort your results. Pagination also ensures that the results you do get can be returned a few at a time.

This also serves as an important failsafe in the API ecosystem. API calls can sometimes cost money, which is often determined by usage. Imagine what might happen if you were to accidentally pull down a Terabyte of data in a single request!

Let’s take a look at what that might look like in action.

const express = require('express');
const bodyParser = require('body-parser');

const app = express();

// employees data in a database
const employees = [
  { firstName: 'Jane', lastName: 'Smith', age: 20 },
  //...
  { firstName: 'John', lastName: 'Smith', age: 30 },
  { firstName: 'Mary', lastName: 'Green', age: 50 },
]

app.use(bodyParser.json());

app.get('/employees', (req, res) => {
  const { firstName, lastName, age } = req.query;
  let results = [...employees];
  if (firstName) {
    results = results.filter(r => r.firstName === firstName);
  }

  if (lastName) {
    results = results.filter(r => r.lastName === lastName);
  }

  if (age) {
    results = results.filter(r => +r.age === +age);
  }
  res.json(results);
});

app.listen(3000, () => console.log('server started'));

In this example, the req.query sets the query parameters. Individual values are destructured using JavaScript’s destructuring syntax. Finally, the filter is run on the results, which are then returned.

So, if you were to make a query /employees?lastName=Smith&age=30, it would return:

[
    {
        "firstName": "John",
        "lastName": "Smith",
        "age": 30
    }
]

This string queries the database to return all employees with the last name of Smith who are the age of 30. The answer is returned with a JSON object.

You can also query for a range of results. Results can be sorted to be displayed in a variety of ways, as well. Consider the following query string:

https://example.com/articles?sort=+author,-datepublished

This returns the results with the authors ascending and the publication date descending.

Final Thoughts on RESTful APIs

Roy Fielding’s definition of the REST standard was one of the key components that made widespread API adoption possible in the first place. As a result, terms like APIs, REST, and RESTful are often used interchangeably. The abundance of jargon can sometimes make deciphering technical writing difficult. Luckily, we’ve recently posted an article on the difference between REST and RESTful if you’re looking for additional clarification.

As with anything having to do with REST, these are more guidelines than hard-and-fast rules. APIs are infinitely customizable, so not even the sky’s the limit. We’re just trying to spare you accidentally calling 6 billion telephone numbers with one accidental query.

Seeing as how API usage and configuration mostly boils down to taste, there’s also a nearly endless array of different ways to optimize your APIs for peak efficiency. You can take a look at some of our other REST API tutorials and API best practices for even more ideas on how to make the most of your APIs.