3 Steps to Make Any Node.JS API Library Better

Jack Yeh
TeamZeroLabs
Published in
4 min readOct 17, 2020

What I wish every Library author would do in the world of API Libraries.

Photo by Brad Barmore on Unsplash

Background

If you are building web services with Node.JS or TypeScript, chances are you have used many community-built API bindings to make 3rd Party API calls. Since these bindings are built by different teams from different backgrounds, their conventions can vary quite a bit.

Below are three things that would drastically improve the usability of an API Library for end users. Library Authors, if you care about gaining more users, please make them happen!

1. Include Type Definition

TypeScript is gaining traction and saving developers a lot of headaches in finding type related bugs. When a request to 3rd party API does not specify its input Shape nor its return Shape, it results in developers putting in lots of:

logger.info(`Result: ${JSON.stringify(response.data, null, 4)}`);

calls to figure out what fields are available. When Request/Response types are specified, it can save end users a lot of time! Another place where this is important is during the constructor calls:

const ImaginaryClient = require('imaginary-client');const client = new ImaginaryClient({
domain: config.domain,
username: config.username,
token: config.token
});

Rather than requiring the user to go back to the Github Readme page once in a while to recall what input signature ImaginaryClient has, or digging into the node_modules folder to figure out how to call this function, if a type definition is included, it would save user a lot of time and we really would thank you for this.

Example: https://github.com/getsentry/sentry-javascript/blob/master/packages/types/src/options.ts

/** Base configuration options for every SDK. */
export interface Options {
/**
* Enable debug functionality in the SDK itself
*/
debug?: boolean;

/**
* Specifies whether this SDK should activate and send events to Sentry.
* Disabling the SDK reduces all overhead from instrumentation, collecting
* breadcrumbs and capturing events. Defaults to true.
*/
enabled?: boolean;

/**
* The Dsn used to connect to Sentry and identify the project. If omitted, the
* SDK will not send any data to Sentry.
*/
dsn?: string;
...
}

2. Provide the ability to Create AXIOS Requests rather than making the call

Making API Library and keeping it up-to-date is time consuming. This is especially true when the API you are referencing is changing constantly as well. Most Library will return a client object that can make request calls for us, this is adequate when the API details are recent.

But, what if the endpoint changes? If a path no longer expects an Id, but now needs an UUID, users can no longer use the same library to make the calls anymore. We have a couple of options:

a. Write our own request calls — Since the Library is taking an input and translating it into an AXIOS or other request/fetch calls, we can use it as a reference and make our own request calls with correct details.
b. Fork the library and fix it in a pull-request — This requires the user to study the library a little bit and make the appropriate fixes and get it merged into upstream.

Both of which are not quick by any means.

Which is why Library authors should add the option of returning the request payload, and let end user do the actual calls themselves.

Example: https://github.com/androozka/zendesk-api-js

try {
const { tags } = zdApi.support.init(options);
const data = { tags: ['tag_1', 'tag_2', ... ] }

const req = tags.add({ type: 'tickets', id: 123, data });

const res = await axios(req);
} catch (error) {
// ...
}

Rather than making the actual calls for us, the library above returns “req”, which is a plain JSON object that we can send to AXIOS to make the call ourselves.

If there are any incorrectly formatted urls, user can immediately get around this issue by monkey patching the req object.

We get to finish our tasks without needing to craft our own requests, or dig deeper into the library to fix the problem. Adopting this pattern saves users a lot of time and headaches!

3. Avoid require() calls in Library usage

Some companies exposes a big set of APIs (think AWS/Google/Facebook scale). Different products under the same company can have upwards of hundreds of API endpoints.

To make sure not all of the files are loaded, many Library chooses to dynamically load different sets of files depending on the inputs.

This is a nice gesture, but it makes testing the code more difficult. Jest for example would complain about require() code being made while the test is being run.

ReferenceError: You are trying to `import` a file after the Jest environment has been torn down.

If the sets of files are truly large, I would recommend the author breaking them into separate packages.

For less than 100 URI endpoints, please do include all of them statically. This makes type definition (from step 1) easier to do, and save us the trouble with testing frameworks!

Conclusion

Node.JS users are blessed with an amazing amount of 3rd Party APIs that is available on NPM and Github. If we can make the above three items standard for popular APIs, we can all become more productive in prototyping and implementation.

  1. Include Type Definition
  2. Allow Request Construction
  3. Avoid Dynamic require() calls anywhere
Photo by Yiqun Tang on Unsplash

--

--

Jack Yeh
TeamZeroLabs

I monitor your full stack deployment in production, so you can sleep at night. Docker | Kubernetes | AWS | Prometheus | Grafana