| Comments

Easily Add Examples to your Node.js Projects with Examplr

Examplr is a lightweight tool for adding examples to your Node.js project. With Examplr, you write the smallest amount of code possible to demonstrate the critical elements of your module: the configuration, the input, and the output.

In this post Evan, the main contributor to Examplr, guides you though adding your first example, advocates why examples are awesome for any project, and shares a real use case often encountered by his team.

Why Examplr?

After reading the brief introduction in your project’s README, a curious developer will want to start hacking from a working example. Examples are invaluable documentation for users and contributors alike, so adding runnable examples should be frictionless and without boilerplate.

Here’s what an example for getting a user with an API client might look like:

import createClient from '../lib'

export default ({apiKey, log}) => async (userId = 'user-id') => {
  const client = createClient({apiKey, log})
  const { name } = await client.getUserById(userId)
  return name
}

Run with Examplr, some convenient magic happens:

  • The API key is automatically passed in as an option. either from the environment variable API_KEY or from a local JSON file (ignored by version control).
  • The user id can be passed in as an argument from the command line.
  • A Pino (Bunyan-compatible) logger is created with colorized formatting.
  • The user name is fetched asynchronously and logged.

Here is what running an example looks like:

Examplr handles configuration options, argument handling, log output, and process execution. Examples can be async functions or return promises. Errors and rejections are caught and logged correctly. The examples live with the code and run in the same environment as the developer.

Add Examples to Your Project Today

I’ll walk though each step of adding Examplr to your project.

If you are already familiar with the concepts here, you can simply install the package and copy the examples folder from the Examplr GitHub project and modify according to the documentation in the README.

  1. Add Examplr as a development dependency with npm

    $ npm install --dev @meltwater/examplr
    

    or Yarn

    $ yarn add --save-dev @meltwater/examplr
    

    Examplr is built for Node.js 8.9.0 or above, but should work on older versions which support async / await.

    Note: the code below uses ES6 module syntax and Babel, but neither are required: see the converted code below.

  2. Create examples/hello.js

    export default ({log}) => (thing = 'world') => {
      return `Hello ${thing}`
    }
    
  3. Create examples/index.js

    import createExamples from '@meltwater/examplr'
    
    import hello from './hello'
    
    const examples = {
      hello
    }
    
    if (require.main === module) {
      const { runExample } = createExamples({examples})
      runExample()
    }
    
  4. Add a shortcut to the scripts object in package.json.

    {
      "scripts": {
        "example": "babel-node examples"
      }
    }
    
  5. Run the example with either npm

    $ npm run example hello
    $ npm run example hello moon
    

    or Yarn

    $ yarn example hello
    $ yarn example hello moon
    

This shows the simplest way to use Examplr, but much more is possible! Take the next steps by looking over the examples folder in the GitHub project.

Invaluable for Our Team

We write a lot of npm modules. Examples serve as anchors for both developers getting to know a new package and the original author.

They function as valuable, up-to-date documentation on a layer above the tests. While tests provide a form of low level documentation, they are a poor starting point and often do not represent real use-cases as external API’s or services may be mocked out. Tests are rigid and focused on good code coverage, while examples are expressive and easy to hack on.

Case Study

We encounter this development cycle constantly.

  • We need a new client package for integrating with an external REST API.
  • Before we can write any tests or mock the responses, we experiment with the endpoints and ensure they work according to the docs.
  • We start by adding the minimal code needed to authenticate and call the API.
  • We then quickly iterate on a few examples to refine the core API.
  • Once the scope and functionality is clear, the bulk of the module, tests, and documentation are written.
  • The initial examples are updated and conveniently provide a powerful debugging tool for the package and external API.

Conclusion

All projects benefit from good examples: they serve well for documentation, debugging, prototyping, onboarding, and more. Examplr has become a very simple but invaluable tool for our team, which is why we decided to open source it.

Special thanks to Liz Howard’s talk at Node Summit 2017 for stressing how useful examples are as developer documentation. And thanks to the Meltwater Labs, the team I work in, for supporting this idea and writing good examples.

If you find this useful, please leave feedback in the comments, or contribute back via the Examplr GitHub project.

Appendix: with CommonJS and without Babel

Replace the imports and exports with require and module.exports, e.g.,

const createExamples = require('@meltwater/examplr').default

const hello = require('./hello')

const examples = {
  hello
}

if (require.main === module) {
  const { runExample } = createExamples({examples})
  runExample()
}

and

module.exports = ({log}) => (thing = 'world') => {
  return `Hello ${thing}`
}

Then, instead of babel-node, use node:

{
  "scripts": {
    "example": "node examples"
  }
}

Comments