Reducing the surface area of the client

I came across this design strategy many years ago when writing client APIs, but it’s a strategy that is worth considering when designing any client API. It’s one I have made extensive use of in our current suite of ASP.NET Web APIs.

The surface area is what the client interacts with when consuming an API. Reducing the surface area is therefore a strategy for reducing what the client needs to interact with. By reducing what the client needs to interact with, your API is simpler and easier to consume by client applications.

Let’s say you have a fairly simple data-entry application that allows the client to add/update/get/delete items such as customers, orders and deliveries (basic CRUD functionality). If we wanted to develop a client API to implement this functionality, we quite reasonably do this by implementing a customer API, an order API and a delivery API. Nothing wrong with this approach. Let’s say that six months later we have added more functionality. We can now add/update/get/delete stock, suppliers and materials. The number of APIs the client now needs to interact with has doubled. From the point-of-view of the client, the API is now getting increasingly more complex to use, as there are a greater number of APIs to learn and use.

But wait. Don’t all those APIs do roughly the same sort of thing? They all provide the same CRUD functionality, just to different entities (customer, order, delivery, stock, supplier and material).

What if we condensed all those CRUD APIs for all those different entities into a single API. That would provide the same level of functionality to the client application, but would also be easier to learn and understand as they only require the one API to interact with.

This is the concept behind reducing the surface area of the client.

In a web application I have been developing, we have a very similar scenario. We have a data-entry web application that provides CRUD functionality to the user. All the functionality has been implemented using ASP.NET Web API. However, the web application only consumes a single API. All POST, PUT, GET and DELETE requests are reduced down to a single API that performs all operations across the entire web application. Not only that, but all the APIs work in the same, consistent manner.

For example, the POST controllers (API) work in the following manner. I pass a single string key on the querystring. This tells the APIwhat type of data is being passed. In the request body is the data itself in JSON format (other formats are available).

Example values for the querystring key could be “addcustomer”, “addorder”, “addsupplier”. Then in the body of the request would be the actual data that represented the entity (customer, order, supplier etc).

Here is example code from the POST request controller.

Hide Copy Code

[HttpPost]
[EnableCors(origins: "*", headers: "*", methods: "*")]
public async Task<HttpResponseMessage> WebPostData(string formname, [FromBody] JToken postdata)
{
//log the request
base.LogMessage(string.Format(CultureInfo.InvariantCulture, "{0}.{1}", GetType().Name, "WebPostDataToServiceBus"));
base.LogMessage($"Formname={formname}");
    //authenticate the request
if (!base.IsRequestAuthenticated())
{
base.LogMessage("Request failed authentication");
throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.Forbidden));
}
//add the request to the service bus for processing
return await this.AddWebTaskToServiceBus(formname, postdata);
}

This same concept can be applied to PUT, GET and DELETE requests. As long as you have a string key parameter that determines the type of the data, then you are able to implement the appropriate logic to process it (e.g. if you know you are adding a new customer, then you de-serialise the customer data and pass it to the database, service bus etc).

This makes your API surface much smaller, which in turn makes them far easier to consume, learn and comprehend. Surely that’s better for everyone.