REST turns 22 years old in a few months. In addition to the API architecture and recommendations outlined in Roy Fielding’s dissertation, we now have two decades of practical application. When designing API projects, it makes sense to build upon the REST best practices and guidelines for web services already implemented by countless others.
This post helps users learn what a RESTful API is and the most common REST API design patterns, development, and principles across several categories. Rather than start anew, build upon this foundation of API guidelines from thousands of successful API companies. Let's get into building a RESTful API.
🔗 HTTP Methods and Status Codes
Let's define REST API. The definition of a RESTful API means you don’t need to use the HTTP protocol. However, the two developed alongside each other, and almost every RESTful API code relies upon HTTP. For that reason, it makes sense to structure your API around the built-in methods and status codes that are already well-defined in HTTP. Here's how you can design, develop, and create an HTTP REST API.
For designing REST APIs, Each HTTP request includes a method, sometimes called “HTTP verbs,” that provides a lot of context for each call. Here’s a look at the most common HTTP methods:
- GET: read data from your API
- POST: add new data to your API
- PUT: update existing data with your API
- PATCH: updates a subset of existing data with your API
- DELETE: remove data (usually a single resource) from your API
We won't discuss all of these in detail, however, you can check out our post on CRUD API Design for more details on CRUD vs REST.
As you design your API, you’ll want to rely on these methods to express the primary purpose of a call rather than adding the purpose to the RESTful API URL. We’ll get to more on that in a bit.
Much as these methods provide the request context from client to server, HTTP status codes help describe the response in the reverse direction.
Some common HTTP status codes include:
- 200: Successful request, often a GET
- 201: Successful request after a create, usually a POST
- 204: Successful request with no content returned, usually a PUT or PATCH
- 301: Permanently redirect to another endpoint
- 400: Bad request (client should modify the request)
- 401: Unauthorized, credentials not recognized
- 403: Forbidden, credentials accepted but don’t have permission
- 404: Not found, the resource does not exist
- 410: Gone, the resource previously existed but does not now
- 429: Too many requests, used for rate limiting and should include retry headers
- 500: Server error, generic and worth looking at other 500-level errors instead
- 503: Service unavailable, another where retry headers are useful
There are many more HTTP status codes and methods to consider, but the above lists should get you well on your way for most APIs.
Use Friendly and Common API Endpoint Names
How do you find and determine the API endpoints of a website? A typical design pattern with REST APIs is to build your endpoints based on resources. These are the “nouns” to HTTP method verbs. Your API design will be much easier to understand if these names are descriptive. For an API design example, if you’re working on a cookbook API, a code example of the following API endpoint example might be: /recipes/
As you add new recipes, you would POST them to the endpoint. To get a list, you use the GET method on the same endpoint. To retrieve a specific recipe, you could call it by its identifier in the URL: /recipes/42
One thing to specifically avoid with friendly REST endpoint names is describing actions as it does not convey any new information about the request.
For example, a verb within the endpoint (i.e., /getRecipes/) would run counter to relying on the HTTP method to provide that context. Our CRUD API Design Recommendations post goes into more detail, including popular topics like plurals and versioning.
GET vs. POST API Bulk Operations
As you're designing RESTful APIs, you’ll want to rely on the HTTP methods and best practices to express the primary purpose of a call. For that reason, you don’t want to use a POST to simply retrieve data. Nor would you want a GET to create or remove data. You should examine your use cases to determine when to use each. Here are several factors that will influence your decision.
- GET requests can be cached
- GET requests are idempotent (in that they can be called any number of times while guaranteeing the same outcome)
- GET requests should never be used when dealing with sensitive data
- GET requests have length restrictions
- GET requests should be used only to retrieve data
- POST requests are never cached (unless specified in the header)
- POST requests are NOT idempotent
- POST requests have no restrictions on data length
Choosing your methods wisely keeps your routes concise and follows fundamental RESTful API practices. Let’s look at a quick example.
Let’s say you have routes that implement basic CRUD functionality(Create, Read, Update, Delete) you would end up with the following routes:
- [POST] /api/recipe - Creates a recipe
- [GET] /api/recipe - Lists recipes
- [GET] /api/recipe/:id - Gets a recipe by ID
- [PUT] /api/recipe/:id - Updates a recipe by ID
- [DELETE] /api/recipe/:id - Deletes recipe by ID
- [DELETE] /api/recipe - Bulk delete recipes
In this example, you only actually have 2 routes "/api/recipes" and "/api/recipe/:id". If you were to just use POST, then you would end up with 6 different routes.
Cache Data to Improve Performance
Caching partially eliminates unnecessary trips to the server. Returning data from local memory rather than sending a query for each new request can improve your app’s performance. GET requests are cacheable by default, however, POST requests require you to specify the cache requirements in the header. Caching, however, can lead to stale data on the client’s browser. Here’s how you would deal with this in the POST header.
In this case, you specify an absolute expiry time for the cached version. Anything beyond the time specified is considered stale and must be updated with the origin server. You can specify a time up to one year in the future.
When using this option, you can set specific directives such as max-age, must-revalidate or no-transform.
With this option, you use the response header to determine caching requirements. The responses’ Date value specifies when the initial response was generated. The Last-Modified date specifies when the response associated with the resource was last changed. The Last-Modified date cannot be less than the Date value.
Plan for Future Use Cases with API Parameters
Naive or simplistic API design can follow all the guidelines above and still not support the use cases that developers will need. It’s important to thoroughly understand how an API will be used and get feedback from collaborators, such as with mock API servers.
Often, when use cases are discovered after an API is built, engineers will create new endpoints to support these unearthed requirements. For example, your cookbook API may need to return only recipes from a specific category, or you want to show the recipes with the least prep time. Rather than create redundant endpoints, plan for smart parameters from the start.
There are three common types of parameters to consider for your API:
- Filtering: Return only results that match a filter by using field age as a parameter. For example:
- Pagination: Don’t overload clients and servers by providing everything. Instead, set a limit and provide prev and next links in your response. Example:
- Sorting: Provide a way to sort or some use cases will still require paging through all results to find what’s needed. Example:
These three approaches can be used together to support very specific queries. Understanding your use cases will help determine the complexity of your parameters.
Borrow From Existing Conventions
While this post does its best to cover overall API design patterns, you’ll want to look at standards and conventions specific to your industry or a specific feature. Very few of us are building completely unique APIs, so there is a lot to learn from others.
Many API standards are built around REST APIs. When you implement authentication for your API, for example, don’t blaze a new trail. There are many options for API authentication patterns, including the well-trod OAuth path, when providing user-associated data. You’ll find standards for API headers and a handful of data formats and models like JSON and XML, among others.
You may be designing microservices APIs, which have their own set of considerations. Everything covered in this post likely still applies, but you’ll want to pay extra careful attention when designing microservices. Each will need to make sense on its own, yet benefit from a combination (loose coupling).
On the other hand, open banking APIs require their own treatment. European standards are the most mature and have a set of design patterns based on those regulations.
Your industry may have its own set of standards or conventions. Even if they aren’t as strict as banking regulations, it’s worth giving proper consideration to a pattern with which developers will already be familiar.
Document with An OpenAPI Definition
As you design your API, it will be extremely useful to maintain an OpenAPI definition as the source of truth. This format, the next generation of the older Swagger file, describes endpoints, request data, responses, error codes, and more. In addition, it can be used to automate tooling across the API lifecycle.
Perhaps the most common use of an OpenAPI document is to generate API documentation, especially an API reference. Since the format outlines the ways an API can be called, it contains all the information a developer needs to integrate with the API. Plus, some API references don’t include essential details like error codes, so OpenAPI encourages accurate documentation. Further, you can generate new docs every time your API changes, so they’ll always be up-to-date.
You can also use your OpenAPI definition to create mock HTTP servers, which allows you to try out your API before you write any code. Circulate the interface amongst your team for early feedback, or validation of the requests from your online REST API client.
Those are just two potential uses for your machine-readable API definition, in which you can create OpenAPI definition files using YAML or JSON. Or, create them much faster with a visual OpenAPI editor. Stoplight Studio can read existing OpenAPI files from any git repo, and you can make edits—or start from scratch—within a beautiful editing environment.
Use Style Guides for Consistency
Some design patterns are a matter of preference. Ideally, you can codify your organization’s approach once, rather than revisiting it each time you create an API. A style guide can keep your company on the same page with API design. In addition to being consistent between APIs, it’s even more important to maintain consistency within a single API.
Some organizations will create a written API style guide. A document that is easily accessible within your intranet helps everyone understand the design patterns you’ve already adopted. However, you can go even further by enforcing your style guide programmatically. Using a tool like an open-source linter, you can define rulesets for your OpenAPI documents.
When you automate your API style guide, you can look for any number of API characteristics: resource and field names, capitalization formats, how you use punctuation, and versioning, among others.
Your style guide, whether written or programmatic, becomes your own guidelines for the design patterns covered here. Help ensure your organization uses HTTP methods correctly, returns appropriate status codes, implements friendly endpoint names, uses smart parameters, and borrows from the existing conventions you’ve already identified.
You’re ready to create fantastic APIs and better understand the API design architecture, so join the world’s leading API-first companies on Stoplight’s API design management platform. For more on API RESTful web services examples and best practices in API REST building, check out this blog on REST to learn more about how to build a REST API.
Now, let's get you creating a REST API!