npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

gofer

v5.3.0

Published

A general purpose service client library

Downloads

1,058

Readme

nlm-github nlm-node nlm-version Build Status

gofer

A gofer, go-fer or gopher /ˈɡoʊfər/ is an employee who specializes in delivery of special items to their superior(s). The special items may be anything from a cup of coffee to a tailored suit or a car.

Wikipedia: Gofer

npm install --save gofer

A base class for HTTP clients. Usable in node, browsers, and react-native. The design is meant to enforce a certain level of consistency in how the clients are configured and instrumented.

Use in browsers might require a fetch polyfill.

If you used gofer 2.x before, you might want to read about all the changes in 3.x.

If you have some old gofer 3.x code that doesn't use classes and Promises, you can read the previous version of the 3.x docs.

API docsWalkthrough

Features

Options mappers

Option mappers are called in the order they are registered in and can potentially do anything they want. This can range from applying defaults over resolving custom api options to injecting access tokens.

Defaults merging

All configuration is just defaults which is one of the things making option mappers so powerful.

The precedence rules are (first wins):

  1. Explicit configuration in the API call
  2. Scoped overrides using client.with(options)
  3. Endpoint-level defaults
  4. Service-level defaults
  5. Global defaults

See the walkthrough below for how these are configured.

Copy with defaults / scoped overrides

You can create a copy of the API with hard defaults using with. This enables a nice pattern:

// We'll assume MyApiClient has an option mapper than knows how to
// properly send an accessToken, e.g. using an Authentication header
const client = new MyApiClient(config);

// After retrieving an access token
const authenticatedClient = client.with({ accessToken: 'some-token' });

// This one will now send an access token
// ~> `curl -H 'Authentication: Bearer some-token' \
//          http://api.example.com/personal/some-id`
authenticatedClient.protectedResource('some-id');

// This one was not changed, so it will not send one
// ~> `curl http://api.example.com/personal/some-id`
client.protectedResource('some-id');

This sounds great, but...

Why not use service specific client libraries?

Well, in a way that's what gofer encourages. The difference is that by basing all client libraries on this one, you gain consistency and unified configuration. Creating a client for a new service often takes just a couple of lines.

Why not use request?

request is a great swiss army knive for making API calls. This makes it an awesome first pick if you're looking for a quick way to talk to a wide variety of 3rd party services. But it's lacking in a few areas we care a lot about:

  • Good, predictable error handling
  • Flexible configuration
  • Instrumentation friendly

Walkthrough

Let's say we need a client for the Github API. The first step is to generate a Github client class:

const Gofer = require('gofer');

const { version, name } = require('./package.json')

class Github extends Gofer {
  constructor(config) {
    super(config, 'github', version, name);
  }
}

The name you choose here ("github") determines which section of the configuration it will accept. It's also part of the instrumentation as serviceName.

Let's define a simple endpoint to get the emojis from Github:

class Github extends Gofer {
  /* earlier stuff here */

  // Every instance of Github will get an `emojis` method, which has access
  // to `this.fetch()`  The `fetch` method works similar to WHATWG/fetch.
  emojis() {
    // fetch(uri: string, options: object?, callback: function?)
    return this.fetch('/emojis');
  }
}

To create an instance, we need to provide configuration. Configuration exists on three levels: global, per-service, and per-endpoint.

const config = {
  globalDefaults: {
    // these apply to all gofers
    connectTimeout: 30,
    timeout: 100,
  },
  github: {
    // these apply for every call made with Github
    clientId: '<VALID CLIENT ID HERE>',
    endpointDefaults: {
      // these only apply for calls to the emojis endpoint
      emojis: {
        connectTimeout: 100,
        timeout: 2000,
      },
    },
  },
};

To make our client a little nicer to use we'll add an option mapper that defaults baseUrl to the public Github API. The options we return will be passed on to fetch.

Github.prototype.addOptionMapper(opts => {
  // opts contains the already merged options, including global-, service-,
  // and endpoint-defaults. In our example opts.timeout will be 2000, etc.
  return {...opts,  baseUrl: 'https://api.github.com' };
});

Finally we can instantiate and make the call:

const github = new Github(config);

// The `fetch`-style:
github.emojis()
  .then(res => res.json())
  .then(emojiList => {
    console.log('Returned %d emojis', Object.keys(emojiList).length);
  })
  .catch(console.error);

// Using the added convenience of req.json()
github.emojis()
  .json()
  .then(emojiList => { /* ... same ... */ })
  .catch(console.error);

You can check examples/github.js for a richer example.

File Uploads

Gofer does not by default ship with support for multipart/form-data file uploads, but it is easy to add, using the form-data module, and an option mapper as shown in this multi-part mapper test.