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 🙏

© 2026 – Pkg Stats / Ryan Hefner

bootwire

v2.0.3

Published

Application and dependencies bootstrap for node.js

Downloads

32

Readme

bootwire

Build Status codecov

Application and dependencies bootstrap for node.js.

npm install --save bootwire

A super-minimal way to boot and compose application dependencies using ES6.

Bootwire is a very simple library that leverages ES6 destructuring to provide a no-black-magick-please way to boot node js applications and perform dependency injection.

Features

  • Support asynchronous boot (ie. wait for database connection to be ready).
  • Simplify tests allowing to mock parts of your application without stub and spies.
  • Predictable top-down flow.
  • IoC with plain old functions and constructors.
  • No require hijacking.
  • No module.exports['@something-magick-and-weird-here']
  • Wire components together not to the IoC container

Getting Started

Bootwire provides a way to create an application context and pass it down to a boot procedure.

The context object is just an object that exposes a few methods to manipulate and use it:

  • $set: set one or many properties
  • $provide: set a property to the result of the invocation of a provider function.
  • $wire: wire a function passing the context as parameter
  • $wireGlob: wire any files matching a glob pattern
  • $waitFor: await for specific dependencies to be wired
  • $get: get a value in the context by key or by path

Using $set and $provide on the context object will ensure that all of its properties will be only set once, allowing to inject providers, services connections, configs and so on during tests.

The boot procedure to which the context is passed is a function that acts as the single starting point of an application.

Dependency injection

As opposed to many IoC containers bootwire takes a radical approach to handle dependencies:

  • Dependencies resolution is not lazy: all of the components are wired together during the boot phase.
  • Dependency injection follows one and only one simple rule: if a dependency is already set it will not be set again.

Which result into an extremely simple way to replace a component or a setting during tests: just set it before the boot phase.

// index.js

require('./app').boot().catch(console.error);
// app.js

const bootwire = require('bootwire');

function bootProcedure({$provide, $set, $wire} /* this is the context object destructured */) {
  $set({
    logger: require('winston'),
    config: require('./config')
  });

  await $provide('db', async function({config}) {
    return await MongoClient.connect(config.mongodbUrl);
  });

  await $provide('userRepository', async function({db}) {
    return new UserRepository({db});
  });

  await $wire(startExpress);
}

module.exports = bootwire(bootProcedure);
// user.routes.js

module.exports = function({router, userRepository}) {

  router.get('/users', async function(req, res) {
    res.json(await userRepository.find());
  });

};

Integration tests are now extremely easy:

// app.spec.js
const app = require('./app');

it('does something', async function() {
  await app.boot({
      config: {port: await randomAvailablePort()},
      db: fakeMongodb // db will not be set during the boot
                      // since is already set here
  });

  // ...
});

And unit tests as well:

const UserRepository = require('./services/UserRepository');

it('retrieves all the users', async function() {
  const repo = new UserRepository({db: {
    find() {
      return Promise.resolve(usersFixture);
    }
  }});

  deepEqual(await repo.find(), expectedUsers);
});

The boot procedure also accepts multiple initial contexts that will be merged together, doing so will be easy to $provide a default initial context on each tests and override it on each test case:

// app.spec.js
const app = require('./app');

const defaultTestContext = {
  config: defaultConfig
};

it('does something', async function() {
  await app.boot(defaultTestContext,
    {
      config: {port: await randomAvailablePort()},
      db: fakeMongodb
    }
  );

  // ...
});

Usage patterns for complex applications

Split bootstrap into phases

// ./boot/index.js

const {promisify} = require('util');
const express = require('express');
const winston = require('winston');

module.exports = async function({$wireGlob, $set, $context}) {
  const config = require('./config');
  const app = express();
  const logger = winston;

  $set({
    config,
    app,
    logger
  });

  await $wireGlob('./services/**/*.wire.js');
  await $wireGlob('./middlewares/**/*.wire.js');
  await $wireGlob('./routes/**/*.wire.js');

  await promisify(app.listen)(config.port);
  logger(`Application running on port ${config.port}`);
};

Top Down $wireGlob

$wireGlob never process a file twice and ensure files are always processed in depth order from the most generic path to the deepest.

It can be leveraged to delegate complex wiring from a general boot file to more specialized procedures.

// ./index.js

const {promisify} = require('util');
const express = require('express');

module.exports = async function({$wireGlob, $set, $context}) {
  const app = express();

  $set({
    app
  });

  await $wireGlob('./routes/**/*.wire.js');
  await promisify(app.listen)(config.port);
};
// ./routes/wire.js

module.exports = async function({$wireGlob, $set, $context}) {
  await $wireGlob('./middlewares/**/*.middeware.js');
  await $wireGlob('./api/**/wire.js'); // NOTE: this will be processed only once
                                       // and from this file even if the path
                                       // matches also the glob from the call in
                                       // ./index.js
};

Bootstrap of many components

Using $wireGlob and $waitFor is possible to create self contained modules that can be wired together without having a main boot procedure knowing about everything.

// ./boot/index.js

module.exports = async function({$wireGlob}) {
  await $wireGlob('./*.wire.js');
};
// ./boot/logger.wire.js

module.exports = async function({$waitFor, $set}) {
  const {correlator} = await $waitFor('correlator');

  $set('logger', new CorrelationLogger(correlator));
};
// ./boot/correlator.wire.js

module.exports = function({$set}) {
  $set('correlator', new ZoneCorrelator());
};

Wiring classes and services

One way to perform IoC without any magic container is to use explicitly the constructor of services to inject dependencies.

Although it may seem a tight constraint it is actually a good way to create independent components that are easy to reuse in different context and applications.

This explicit and manual injection is intended and is necessary to achieve one of the goal of bootwire: don't require components to depend on the dependency injection framework.

// services/UserRepository.js

class UserRepository {
  constructor({db}) {
    this.collection = db.collection('users');
  }

  find() {
    return this.collection.find().toArray();
  }
}

Note how the UserRepository class is completely usable both with bootwire:

// boot/index.js

module.exports = function({$provide}) {
  await $provide('db', async function({config}) {
    return await MongoClient.connect(config.mongodbUrl);
  });

  await $provide('userRepository', async function({db}) {
    return new UserRepository({db});
  });
};

And without bootwire:

// tasks/dumpUsers.js

async main() {
  const db = await MongoClient.connect(process.env.MONGODB_URL);
  const repo = UserRepository({db});
  const users = await repo.find();
  console.info(JSON.stringify(users, null, 2));
}

main().catch(console.error);

Api

Classes

Functions

if (require.main === module) { app.boot() .catch((err) => { // Something extremely bad happened while booting console.error(err); process.exit(1); }); }

module.exports = app;

describe('app', function() { it('runs', async function() { const port = await getRandomPort();

await app.boot({ config: { port } });

await request('http://localhost:${port}/health'); // ... }); });

App : Object

App is a bootable application.

Kind: global class

app.boot(...initialContext) ⇒ Promise

Start an application with an initialContext

Kind: instance method of App
Returns: Promise - A promise resolving to Context when the boot procedure will complete.

| Param | Type | Description | | --- | --- | --- | | ...initialContext | Object | One or more object to be merged in the context and build the initialContext. Note that any function already present in the prototype of Context (ie. $wire, $set, $provide) will NOT be overriden. |

Context : Object

Context is the main application context object. It acts as dependency container and is intended to be passed down through all the initialization procedure.

Kind: global class

context.$context ⇒ Context

Returns the same context instance.

Useful in factory and provider functions to destructure both the context and its internal properties.

ie.

module.exports = function setupRoutes({app, context}) {
 // NOTE: config === context.config

 app.get('/users', require('./users.routes')(context));
}

Kind: instance property of Context
Returns: Context - the context object itself

context.$set(keyOrObject, value)

$set sets one or more keys in the context if they are not already present.

ie.

$set('logger', winston);
$set({
  config: require('./config'),
  logger: winston
});

Kind: instance method of Context

| Param | Type | Description | | --- | --- | --- | | keyOrObject | String | Object | a string key in case of single assignment or a key-value map in case of multiple assignment. | | value | Any | the value to be assigned in case a string key is provided. |

context.$provide(key, fn) ⇒ Promise

$provide allows to assign to a contpext key the result of a function (provider) that is invoked with context as parameter.

If the context key is already taken the $provide returns without doing anything.

The function to be evaluated can be synchronous or asynchronous. In either cases $provide returns a Promise to wait for to be sure the assignment took place (or has been rejected).

Kind: instance method of Context
Returns: Promise - a promise that will be resolved once $provide has completed the assignment or refused to assign.

| Param | Type | Description | | --- | --- | --- | | key | String | the key to be assigned | | fn | function | the function to be evaluated. Context will be passed as param to this function. |

context.$wire(...fns) ⇒ Promise

$wire invokes one or more asynchronous function passing the context as first parameter.

Kind: instance method of Context
Returns: Promise - a promise that will be resolved once fn will complete.

| Param | Type | Description | | --- | --- | --- | | ...fns | function | the function or functions to be evaluated. Context will be passed as param. |

context.$wireGlob(...patterns) ⇒ Promise

$wireGlob requires and wires files by patterns from the caller folder.

ie.

await $wireGlob('routes/*.wire.js');

Kind: instance method of Context
Returns: Promise - A promise that will be resolved once all the files are required and wired

| Param | Type | Description | | --- | --- | --- | | ...patterns | String | One or more pattern expression (see https://github.com/isaacs/minimatch#usage for help) NOTE: path patterns are relative to the caller file and not to process.cwd() |

context.$waitFor(...keys) ⇒ Promise

$waitFor wait for the resolution of the dependencies passed as argument and then it returns the context;

const {logger} = await $waitFor('logger');

Kind: instance method of Context
Returns: Promise - A promise resolving to the context once all the dependencies are ready

| Param | Type | Description | | --- | --- | --- | | ...keys | String | A list of dependencies to be awaited |

context.$get(key, [defaultValue]) ⇒ Any

Get a value from context by key or path.

const context = await app.boot();

const port = context.get('config.port');
const info = await request(`http://localhost:${port}/api/info`);
// ...

Kind: instance method of Context
Returns: Any - the value if found or defaultValue.

| Param | Type | Description | | --- | --- | --- | | key | String | a single key or a path of the form 'key1.key2.key3'. | | [defaultValue] | Any | a value to be returned if the key is not found. |

bootwire(bootAndWireFn) ⇒ App

Build a new App that will use invoke the boot and $wire procedure passed as parameter on boot.

Example usage:

const bootwire = require('bootwire');
const app = bootwire(require('./src/boot'));

if (require.main === module) {
  app.boot()
    .catch((err) => {
      // Something extremely bad happened while booting
      console.error(err);
      process.exit(1);
    });
}

module.exports = app;

Example tests:

const app = require('../..');

describe('app', function() {
  it('runs', async function() {
   const port = await getRandomPort();

   await app.boot({
     config: { port }
   });

   await request('http://localhost:${port}/health');
   // ...
  });
});

Kind: global function
Returns: App - A bootable App instance.

| Param | Type | Description | | --- | --- | --- | | bootAndWireFn | function | The function to be called. |