Building Consistent RESTful APIs

When it comes to building RESTful APIs, the applications are limitless while the standards and/or best practices remain the same across…

Building Consistent RESTful APIs

When it comes to building RESTful APIs, the applications are limitless while the standards and/or best practices remain the same across different stacks. This is an attempt to portray some of those standards and best practices (highly opinionated).

Authorization

Representational State Transfer (REST) effectively invalidates sessions and as such, our applications require another way of authorizing users trying to access it. One of the most commonly used tools for authorization is JSON Web Tokens-JWT. Once a user logs in or signs up to our application, a token is assigned to that user for a period of time (the length depends largely on the security concerns of your application). A good practice is to add a unique identifier to the token so each request to our application can serve the particular user making that request without asking for extra parameters from the user.

For example, if I am logged in using JWT, and I attempt to retrieve all transactions I have made on the application, the application should be able to provide ONLY my transactions based on my JWT authorization token.

Without going into implementation details, one can achieve this by having a service intercept incoming requests, process the JWT token and set a current_user object or variable accessible throughout the application.

Resources, Actions, and HTTP Methods

In REST we have resources, which can represent database tables, e.g companies, users, posts, messages, etc. Every resource has a list of actions that can be performed on it. These actions are determined by the HTTP method on each request to that resource. HTTP Methods are also referred to as HTTP VERBS because they perform/determine actions. Given a resource, transactions, below are some of the most common corresponding HTTP Methods and actions.

GET — /transactions (get all transactions)
GET — /transactions/:id (get a single transaction)
POST — /transactions (create a new transaction)
PATCH — /transactions/:id (update parts of a transaction)
PUT — /transactions/:id (update a transaction)
DELETE — /transactions/:id (delete a transaction)

Depending on the scale of your application, you may need to extend the actions available on your resource by custom endpoints. Custom endpoints can either affect the entire resource (collection) or a member of the resource. For actions affecting the collection, it is as follows:

GET — /transactions/users (get all users that have made transactions)
GET — /transactions/failed (get all failed transactions)

For a member, assuming we have a separate but related resource for a transaction details:

GET — /transactions/:id/details (get the details of a transaction)
POST -/transactions/:id/details (create details for a transaction)
PUT -/transactions/:id/details (update the details of a transaction).

200 — OK

The request was completed. You can’t go wrong with a 200 status if the action requested for was completed successfully however, there might be more appropriate status messages depending on the action.

201 — Created

Ideally, this should be returned whenever a new object is created (mostly with a POST request) alongside the object that was created. Returning the object that was created is a personal preference as it grants access to the newly created object immediately.

204 — No Content

This can and should be returned after a DELETE has been completed on an object.

401 — Unauthorized

This is appropriate when a user tries an action that requires authorization but the request doesn’t possess that authorization.

404 — Not Found

Highly unlikely but if a user requests for a resource that doesn’t exist, 404 is the appropriate status to respond with. Albeit based on your use case, returning 200 — ok and an empty result may be more appropriate.