The new Style Guide Projects feature is now available in preview mode for new workspaces. If you have a Professional or Enterprise level account, contact us to learn more. Read this documentation to learn more about Style Guide Projects here.
REST turns 22 years old in a few months. In addition to the architecture and recommendations outlined in Roy Fielding’s dissertation, we now have two decades of practical application. When designing APIs, it makes sense to build upon the best practices already implemented by countless others.
This post identifies the most common REST API design patterns across several categories. Rather than start anew, build upon this foundation of API guidelines from thousands of successful API companies.
🔗 HTTP Methods and Status Codes
By the strict definition of REST, you don’t need to use the HTTP protocol. However, the two developed alongside each other, and almost every RESTful API 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.
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.
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 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 Endpoint Names
A typical design pattern with REST APIs is to build your endpoints around resources. These are the “nouns” to HTTP method verbs. Your API design will be much easier to understand if these names are descriptive. For example, if you’re working on a cookbook API, you might include the following endpoint: /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
As you design your API, you’ll want to rely on the HTTP methods 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 recipe
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 parameters. 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, including the well-trod OAuth path, when providing user-associated data. You’ll find standards for API headers and a handful around data formats 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 around 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 with 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 validate the requests from your API client.
Those are just two potential uses for your machine-readable API definition, 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.
Now you’re ready to create fantastic APIs, so join the world’s leading API-first companies on Stoplight’s API design management platform.