Sponsor: Do you build complex software systems? See how NServiceBus makes it easier to design, build, and manage software systems that use message queues to achieve loose coupling. Get started for free.
Everyone seems to love best practices or rules for designing REST APIs. But is all this guidance good advice? Let’s find out.
YouTube
Check out my YouTube channel, where I post all kinds of content accompanying my posts, including this video showing everything in this post.
“REST” API Rules
I stumbled upon a post outlining rules around how you design REST APIs. These posts are often found on LinkedIn, and I usually shake my head when reading them, so I figured I’d review this one and give some feedback.
I shake my head because some of these will seem pretty standard, but I will be pushing back on some of them. Just because something is popular doesn’t mean it makes sense.
Plural Nouns for Collections
This is pretty common when the URI for a collection is a plural noun. For example:
GET /products # get all the products
GET /products/{product_id} # get one product
Before I push back on this common conversion, let’s look at another “rule”.
Path Segments
Don’t add path segments that aren’t needed. The example given in the original post refers to Etsy’s API, where the “shop_id” is a part of the URI, even though the listing_id is unique. Example
GET /v3/application/shops/{shop_id}/listings/{listiing_id}
The claim is that adding the shop_id to the URI is unnecessary since the listing_id is globally unique. Does this make sense? Sure, if you’re thinking about nouns, entities, and your database structure. And that’s exactly the problem with these first two “rules.”
A resource can be whatever you want it to be. However, the industry trend is to model your HTTP API (notice I didn’t call it REST) based often on the structure of your database or the relationship between entities. Nouns are often entities within your system, as I illustrated earlier. Products, Ships, Listings, etc.
If you’re modeling off entities as nouns and then using HTTP Methods (they’re not verbs), all your API revolves around CRUD. These “rules” and current industry trends are modeling an HTTP API around CRUD operations against a database.
I call this trend URI-driven API, focusing on building a URI structure around entities/databases and CRUD. This is NOT how you have to design your HTTP API.
Instead, URIs can be totally opaque to the client if you’re using hypermedia. Hypermedia is as old as the web and is used on this page you’re looking at. However, when developing HTTPI APIs, most of the industry has completely ignored it. However, it eliminates the first two rules.
Here’s an example of a response of a resource that’s an order.
The actions collection describes all the available resources that relate to this current resource based on its current state. You’re able to cancel the order since it’s pending. At design time (developing) you’d be looking at the actions collection for an item where the name is CancelOrder and then likely showing the relevant UI so the end user could perform that action. The URI is given to you. You’re not constructing a URI. Hence, the URIs are totally opaque to you. Bikeshedding over URi structures is no longer relevant.
Check out my post Smarter Single Page Application with a REST API for more.
Don’t return arrays as top-level responses.
This is good advice because it’s limiting. You’re handcuffed if you return an array at the top level of your response. You can’t add anything else that’s meaningful to the response.
[{ ...order1...}, { ...order2...}] # Bad
{ "orders": [{ ...order1...}, { ...order2...}], "count": 2 } # Good
If you wanted to add some pagination to the response, such as total property, page, etc, you couldn’t add this to this type of response. Instead, return an object and a property that contains the array/collection. That way, you can always evolve the response and add more if required.
Strings for all Identifiers
# BAD { "id": 123 } # GOOD { "id": "123" }
I agree and disagree with this one. The point is that your identifiers could change if your system evolves. Defining them as strings gives you more options if your system evolves and you have some type of partitioning. Let’s say your identifiers instead have a hyphen in them for partitioning purposes.
I can’t entirely agree with this for the same reason as with hypermedia earlier. If you’re not constructing URIs, you likely don’t care about identifiers because relevant URIs for other resources are generated by the server and provided to the client. The client becomes oblivious to identifiers since they are already in the URI that was given to it.
Don’t use 404 for Not Found.
A common practice when you design REST APIs is to return a 404 Not Found when a resource doesn’t exist. As I’ve indicated before, the caveat is that most view a resource as explicitly being a record in a database.
The advice in the blog post discusses consistency in a distributed system. The problem is that we’re talking about resources as entities, and in the example given, two systems communicate by calling each other via an HTTP API. The example is very context-specific about the author’s experience and how the system was designed.
Given the author’s context, this entire topic can get very opinionated, and I don’t entirely disagree. However, like I often say, some problems you have don’t need solutions; you just need not have the problem in the first place.
“If you treat 404 as success”. This is the root of the problem. You could have your system return a 410 Gone, which was recommended, but if you treat that as success, you’re in the same situation.
URIs
URIs can be whatever you want them to be. They do not need to be an “entity” or a record in a database. This trend is so commonplace that it has led to so many “solutions” to problems you don’t necessarily need to have if you view resources differently.
A Uniform Resource Identifier (URI) is a unique sequence of characters that identifies an abstract or physical resource,[1] such as resources on a webpage, mail address, phone number,[2] books, real-world objects such as people and places, concepts.
https://en.wikipedia.org/wiki/Uniform_Resource_Identifier
I get the fact of consistency and using the common way the industry as a whole does something. But you don’t have to, nor does it necessarily fit or make sense to do so within your context.
URI-driven HTTP API design is what we currently call REST. It’s URI-driven and entity-centric CRUD with JSON. That’s what it is.
Almost all the “best practices” revolve around that idea. That isn’t how all HTTP APIs are designed, nor is that REST. Be pragmatic when you design REST APIs. The fact is, they aren’t REST APIs at all. They are an HTTP API. And that’s fine. Call it what it is. That might mean you follow the industry trend. It may mean that you don’t. What everyone is doing doesn’t imply it’s correct in your context.
Join CodeOpinon!
Developer-level members of my Patreon or YouTube channel get access to a private Discord server to chat with other developers about Software Architecture and Design and access to source code for any working demo application I post on my blog or YouTube. Check out my Patreon or YouTube Membership for more info.