How to improve your APIs
As a developer I saw, wrote and debugged a lot of APIs, SOAP, REST…REST wanna be and with this article, I want to present to you some little tricks that will make your life easier (I hope).
Just to be clear: I do not pretend to be a guru on this subject, and all the information from here, are gathered from multiple sources and from my personal experience (“It worked for me, in my specific case”). Don’t make a rule of every point I am trying to make here!
Now, let’s start:
- Have a standardization in naming your routes! REST it’s all about resources, use the correct verbs to help you and use the routes to work with your resources.
For example, if you use api/cars with a GET, it’s easy to understand you want all the cars. If you use it with a POST you want to create a new resource. If you use api/cars /15, for me it means in case of a GET that you are expecting the car with the Id 15, or in case of a PUT, I will be looking at the payload for the content that you want to update the resource with the Id 15. For deletion, it’s pretty simple: in case of no id, it means delete all, in case of an id that is provided, it means the deletion only of the resource with that specific id.
Others prefer for example “Google’s way” of naming the routes, something like api/cars/15:makeitred. I find it pretty ugly, but I might be subjective. It’s nothing wrong with it, but you know the saying: “Write the code as if it would be your project”. - Use the response codes (HTTP status codes). 200 and 400 are not enough. It’s says nothing and it’s a bad practice in my opinion.
- When you create a resource, you should send back 201; when action is acknowledged the response should be 200; when you search for a resource that is not there you should return 204
- Use 400 ONLY for bad requests (not for everything); 401 for unauthorized; 403 for forbidden; 404 if the route is not found; 405 if the method is not allowed; 409 if it’s a conflict; 413 if the payload you sent is too large;
- Use 500 for all internal errors or issues that your system had and could not send back a 2xx; 502 if the systems that you called sent back something that stopped you from giving back a 2xx (such as a 4xx or a 5xx)
These are only some examples that I use. Do not be afraid of “saying too much”. If you send back only 2 status codes, doesn’t make your system more secure, it just makes it bad from the point of view of the design. You can find all the status codes here - Standard error messages.
You shouldn’t send back object A in some cases and object B in others with the same status code. If you are sending back a 400 for example, in all the cases, the object should look the same. This doesn’t mean that you should give all the information back in the error message, but you should send back enough so that your client will understand that something is wrong, something from the call in our case of 400 and more information for debugging purposes or audits, should be logged in your system (from the caller, timestamp to what they sent and what exception or error was generated) - Be careful when using nested resources.
I usually try not to do that, but it’s your call! Example: GET /api/type/bmw/cars/ or GET /api/cars?type=bmw ; I would prefer the second one. - Use pagination on your endpoints.
It will improve your overall performance and your app’s user experience. An example of route would look like this: /cars?page=1&pagesize=20 . This will reduce the payload size which will make your system faster. - For filtering add the filters in the query string.
Which one looks better: /api/cars/soldwithdiscount or /api/cars?soldwithdiscount=true . In my opinion, the second one is way more explicit and clear. For the new clients of your API, or for the new devs that will join your team, the first one might be a little bit misleading; “soldwithdiscount” — is it a resource or not? — they will ask. - To reduce the payload size I also recommend GZip compression / change Accept-Encoding header to gzip. Both are supported right now by almost all browsers. (For the .net devs, I have only one red flag: The gzip compression didn’t work for me, last year (2020) on IIS Express. It worked on-prem and on Azure, so hopefully the issue was fixed, or it was only one mistake on my side)
- Authorization and authentication (or the other way around).
It’s extremely easy, but people still implement it wrong.
- Authentification is the “process” in which you log in (the system confirm/understands who you are). If your credentials are invalid then you should get back a 401.
- Authorization is the “process” in which you as a user can see/modify a resource. If you are logged in but don’t have the access/don’t have enough permissions to do a specific call you should get a 403. - If you get a lot of requests for the same static resources, enabling caching will increase your performance significantly
- Prevent the abuse of your endpoints.
Basically what I am trying to say is that on any API you should add a few standard “things”. This is one of those “things” (havin` a nice place beside the health checks and good monitoring). If a user clicks 100 times in 30 seconds with full of rage on a button, or if a malicious guy created a client to make a DDoS on your API, you should have a mechanism in place that ignore the identical calls or do something with them, not block the system and force it to process the same information 100 times. - On big resources, try to use PATCH instead of PUT. With PATCH, you send only the props that should be updated, instead of the entire entity/resource.
I could go on and on, but I think those 11 steps are the most important things that make a difference. It’s easy to create APIs, it’s easy to change them, but you will need to always put yourself in your consumer's shoes also. Do you still like your “baby” seeing it through their eyes? The business will always evolve, but we as responsible developers, should always think about quality and performance. Don’t accept the idea of building fast, only for an MVP. Bad design isn’t good enough for an MVP. Bad design it’s just…bad design in any app. Don’t create an iceberg and leave the project. In the end, the reputation of the company you represent and eventually your will suffer.