The Best Way to Organize Your Web Api

For several years I’ve been building APIs for web services. In this time, the most recurrent question that has come up is “how do we structure this API”? After years of working in this area, I’ve made many mistakes and learned many lessons. Now, I feel I’ve come up with the best approach.

The most common response to the question of organizing an API has been “Make it RESTful!” While I think that REST is a fantastic pattern for implementing an HTTP API, I don’t think it’s a one-size-fits-all solution.

I prefer to completely separate the public API (system-to-system) from the internal API (browser-to-server).

The Public API

The public API is what other systems use to integrate with your service. For the public interface, I prioritize readability, consistency, and performance.

I recommend implementing the REST pattern. By using this pattern, the API can be easily readable which helps the integrating system can easily grok what resources they need to access.

Token authentication is a simple and efficient mechanism for authenticating clients to make requests. This method assumes there is an existing authenticated context where an operator can obtain a secret token. This token is then passed as a header in API requests. This method of authentication is stateless and so the token must be supplied in every request.

Since the public API is open to any client with an API key, it is important consider the performance of your server. As well, you should consider rate-limiting these endpoints to prevent traffic from overwhelming your system.

The Internal API

The internal API is how your web application communicates with your webserver. For the internal interface, I prioritize developer productivity.

A likely scenario is that your app has some complex javascript running in the browser that must make requests to your server to populate the user interface. If you’re a REST purist, you could argue that both your public and internal APIs should share the same interface. This seems like a very clean and pragmatic design but there are many pitfalls:

What I recommend is to have all internal API endpoints implement an RPC style interface and rely on stateful session authentication.

The RPC Style Interface

With an RPC style interface, we have API endpoints with the following structure:

/rpc/topic.action
/rpc/messages.markAsRead

By structuring your endpoints in this way, the interface is well organized and implementing a new action is straightforward as there is no wasted energy attempting to model your problem using a particular pattern. Additionally, the server can perform complex operations before returning the resulting data which can be difficult to do with other API patterns or might require the client to make several requests.

Since I’m prioritizing developer productivity above all else, I ensure that the server can accept data in many formats. All endpoints accept most HTTP verbs (GET, POST, PUT), and data can be sent as query parameters or in the body of the request as JSON. By doing this, developers can quickly build against an existing or new endpoint. If consistency is a concern, a coding style guide can enforced during code reviews.

Session Authentication

With session authentication, there will be one endpoint where the browser can exchange a username and password for a session cookie. All future requests from this browser will be authenticated until the cookie expires or is deleted.

By using session authentication, we can lock down every internal endpoint to check for a valid session cookie. Invalid requests are rejected and valid requests can be given the context of the current user. By doing this, the developer workflow is quite productive since, if you’re using an ORM, it’s quite simple to query relations like user.messages or restrict all queries to the context of the current user.


By no means am I saying this is the only approach to constructing an API but I think splitting out public and private APIs is a great way to increase productivity in teams and focus on the right problems for each domain.

Comments