REST API best practice: One Database call per Route

- By Avi Cavale on June 17, 2016

Shippable has been on a roll for the last 8 months. We scaled our team by 3X, had the best-ever release of our continuous integration and delivery platform on February 29, and we’ve continued launching 10+ features every month since then. 

As we scaled our development team, every new developer joined us with preconceived ideas about how software is developed. Unfortunately, software development is pretty inefficient at most places, and this is one of the main reasons we started Shippable. We at Shippable do things differently. We focus on shipping code faster and faster, and we hold some principles very close to our heart. 

One of our strongest beliefs is in pure REST APIs. This means we follow the cardinal rule: thou shalt not make multiple calls to DB objects from inside a single RESTable route. So when any new developer joins our team, his first question is : Why do we call our API from within our API?

This question makes perfect sense. Our API has a connector to the Database and we use Object-Relational Mapping (ORM) to access data with methods like find, findById etc. Consider this example: I want to get all Projects that an Account has access to. However, keys for the project should not be returned unless the Account is an Owner of that Subscription. The route will be as follows:


where 123 is the accountId.

When this route is called, we first get a list of Projects using a simple call to the Database: projects.findAll({accountId:123}). This will return all projects the account has access to, irrespective of whether the account is an owner. The question is, how do we find all projects for which the account is an owner, so that we can remove keys? Should we:

1. Make another call to the database to check if it is an owner: accounts.findById({id:123})

2. Make an api call to /accounts/123

On the surface, the first option looks better. You can talk to the Database directly and have already made one call, so why not a second one? 

Of course, we chose the latter. And here's why.

The first reason is that I am a REST nazi. I believe that making multiple calls to get database objects inside a single RESTable route dilutes it. But that's a weak reason and similar to saying - Just cause. So lets try to get more concrete.

The REST of my reasons (pun intended) are:

API versioning: If there is only one place in the code where CRUD operations for a DB object are called, versioning of API becomes simpler. On the other hand, if each route makes multiple DB calls and the underlying object changes, you will need to go to multiple places and change code and versioning cannot be leveraged.   

Security: Any security rules (such as not returning keys if the Account is not an Owner) are enforced in one place as opposed to depending on developers to remember the rules each time they make database calls. This reduces the probability of introducing security bugs.

DB Indexing: You know exactly how the database is being queried since each object is only queried from one route. It is easier to figure out how to best index the database.

Load BalancingIf you make API calls in an async series call, load balancers distribute your get calls over multiple API nodes. This does not happen if database calls are made directly, though connection pooling will offset this disadvantage to some degree. 

Code reviews: Code reviews becomes simpler since for any route, you're only reviewing code and optimizing CRUD for one database object.

On-boarding for new developers: New developers can just call the API layer without worrying about underlying data structures. This makes them much more confident about making changes and starting to contribute.

I believe this approach provides better abstraction and allows us to innovate faster.

Some of you might say that the same thing can be achieved using call backs in node.js. While callbacks have some of the above advantages, they do not address all of them. Plus, not everyone uses Node.

Trust me. Go for the best and stay true to REST!

Topics: nodejs, tips, REST API, Workflows