Most API libraries define one method for each endpoint. If there is an endpoint for getting user information, you might have a method like:
getUserInfo(userId);
That simple method is often a wrapper around another function that actually does the api call:
apiCall('/user/info', {userId: userId});
Another option I see less frequently is for the library to only contain the method that makes the API call.
The first method only has the advantages of code discoverability, but it isn’t that big of an issue for dynamic languages. Some say it also removes the need for the developer to read documentation for the APIs, but why would you ever want somebody using APIs without reading the documentation?
The second method has three advantages that I see:
- The library is dramatically smaller
- Methods don’t need to be updated if the API ever changes
- There is no need to maintain documentation for the library; the API documentation fulfills that purpose
But I don’t see the second method used very often, so perhaps I’m missing something. When is it appropriate or inappropriate to use either method of writing API libraries?
1
I would always vote for the getUserInfo(userId)
style methods to be included, for these reasons:
- discoverability;
- less written documentation needed (as the methods, their context, their names and their parameters document themselves mostly);
- methods describe available functionality;
- library may not be significanltly smaller as these methods are themselves really small;
- hiding details such as JSON <-> Object conversions and error handling;
- save different users from having to write similar code to use the API;
- if the API changes, only the library has to change. Otherwise, all code ever written for the API has to change.
Furthermore, with such methods you get the ability to:
- validate arguments before sending the query;
- do custom formatting on input arguments and custom parsing on return values, invisible to the library user;
- put the methods in logical classes and namespaces: cleaner API;
- use and return custom objects that encapsulate data commonly found together (such as a
User
orBlogPost
objects); - include workarounds for bugs or provide a logical/consistent API view of an illogical/inconsistent API (design);
- convert error return values into exceptions.
getUserInfo(userId)
looks like a simple local function call. You don’t care if it’s doing any REST or SOAP connection to retrieve the user, or if’s retrieved from a database. At this level of abstraction it may be valuable to do so; when you’re dealing with business rules involving users, you are not necessarily interested about the details of how this data is retrieved. Also by designing your code this way you have the possibility of changing these details (e.g. modifying the REST interface) without changing all the code that calls this function.