Consuming third-party JavaScript APIs (some like to call them SDKs) from some authors can be somewhat of a pain. Many are poorly documented, unintuitive to work with, and don’t provide enough hooks to be useful in many situations. Today I’m going to go over my guide for building great JavaScript APIs.
Resourceful
In REST, URLs are constructed with actions primary revolving around a resource. For example, if we wanted to fetch a user from a server, our URL might look like http://localhost/users/12345, where users is our collection container pointing to the user resource. REST is very popular because URLs are human readable; we can make a pretty good guess of what is happening behind the scenes just by the URL structure.
Making our APIs resource-oriented is very similar to being object-oriented, and so our ultimate goal is for our methods to describe objects and entities, not actions. In many userscripts you might see something like this:
1 2 3 4 5 6 7 8 9 10 | |
This is supposed to be an API for querying Twitter tweets via JavaScript. While the script may be operational, it is contrary to our goal of being resource-oriented because the method is called search. How can we make this resource-oriented? By describing what we want back, not what we are doing:
1 2 3 4 5 6 7 8 9 10 | |
Our API now describes a resources and not an action. Depending on how you form your API, you could change the naming pattern and still be resourceful:
1 2 3 4 5 | |
Now take a moment to look at that last example. You can see that our method call is an action. Isn’t this contrary to what we just said? Not really, because the object we are calling it on directly describes the resource we are getting. Just like in REST, actions (HTTP verbs) determine the representation of a resource; in designing our API we can take advantage of this naming style to improve several aspects of our API.
Readability / Self-Documenting
In the previous paragraph, I mentioned that our naming conventions can improve our API, and one of those aspects is readability. Take this API for instance:
1 2 3 4 5 6 7 | |
This single method is doing quite a bit. It performs a search, returns the results, and appends each of those results to the DOM. Readability score: 0. We can design our API better than this:
1 2 3 4 5 6 7 | |
Definitely better. We have split the fetching of results from the processing of the results, and improved the readability of interacting with the API by allowing the results to chain through a Promise-style interface. You could even take this a step further and provide an asynchronous iterating mechanism to process tweets when the results come back. This will save the user from having to build a loop for every call to get tweets:
1 2 3 4 5 6 | |
Very clean and easy to read.
Unobtrusive
Our next goal is to be unobtrusive, and by that I mean leave the user and their code alone:
The API should only be accessible through one global variable, which leads to #2:
Do not leak variables into the global scope.
Provide the user a way to put your API into a no conflict mode.
If you have a global variable for the fetching of tweets, such as
Tweets, allow the user to easily remove the global variable and make it a part of their application. Many APIs do something like this:<script src="/scripts/tweets.js"></script> <script> App.tweets = Tweets.noConflict(); </script>Calling
.noConflict()on the API will remove theTweetsnamespace and return all its original functionality, which the consuming application uses to extend their own application. Unobtrusive win.Unless the specific purpose of your API is DOM manipulation, don’t do it.
With our Twitter API example, avoid forming the API like this:
Tweets .get( 'lolcats' ) .appendTo( $('#twitter-container')[0] );Working with the DOM is outside the scope of your API, and even though it may be convenient, your mechanism may not be able to handle all the use cases users may need when working with the DOM. Best to just provide them as easy a mechanism as possible for getting your API data into the DOM.
If possible, avoid creating or changing events in userland code. Provide hooks instead.
Reduce or eliminate dependencies.
It is not a good practice to always assume that the user of your API will always have jQuery available or doesn’t need IE7 support. Remember these things when creating your API. The goal is to reduce dependencies, which has positives and negatives. Eliminating dependencies reduces having to manage upgrades and bugs outside your control, keeps users from having to manage this as well, and also opens up the amount of users able to consume your API. If you force your users to have jQuery in order to consume the API, you could lock out many developers who cannot use jQuery or will not for many reasons. It is best to eliminate dependencies as much as possible.
That being said, having dependencies could reduce the amount of code you need to write, whether that be from offloading tasks to another third-party script or lessen the amount of cross-browser compatibility code. Browser support also comes into play here. Make reasonable decisions on the what browsers you are able to reasonably support up front, and maybe attempt to support more browsers in the future.
Extensible and Granular
I cannot tell you how many times I have used third-party plugins and APIs and not have them work exactly as needed. As a JS API developer, it is almost impossible to forsee all the situations, environments, and quirks that your script may have to work within. API consumers will know much more about their environment than you, so the easiest solution is provide hooks into your API that a user can latch onto and seize control of a situation. Create as many hooks as possible, even though they may not seem valuable to you at the moment. For example, you could use an event-style system to give the API consumer the ability to make changes at critical stages in the pipeline:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | |
By providing hooks into the entire lifecycle of a method call in our API, we can allow the user to do incredible processing with easy mechanisms. Integration into existing applications is made much easier if you provide hooks into your API everywhere possible.
Consistency / Conventions
Last but not least, if you adopt a style for your API, make sure that style is consistent across the entire codebase. Users can become very aggravated if they become accustomed to interacting with the API in one way, only to have to switch styles when working on another method from it. Pick a style and stick with it.
Conclusion
Designing JavaScript APIs can sometimes be difficult, but building them in these ways will ensure that you have made your API as friendly, intuitive, and powerful as possible.
Have I missed or would you add anything to this list?