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

microboot

v3.0.3

Published

Boot up your app in wee little modules with the help of glob.

Downloads

36

Readme

microboot

Build Status Coverage Status Dependencies npm version

Boot up your app in wee little modules with the help of glob.

const system = await microboot([
  'boot/databases',
  'boot/logging',
  'api/**',
  'listeners'
])

console.log('Service booted!')

Contents

Introduction

microboot is a small helper designed to facilitate booting up complex applications that we all have. Database connections need to be made, logging services initialised and it all ends up happening nastily at the top of the file.

Using microboot helps you organise your start-up by initialising your app step-by-step, reading file structures to grab everything you need.

Uses

I use this tool for booting APIs and microservices within our architecture. microboot lends itself really well to this as each endpoint can be instantiated in a single, isolated file and the only configuration needed to hook these together is microboot.

// index.js
require('microboot')('endpoints')
// connections/api.js
const express = require('express')
const api = express()
api.listen(3000)
module.exports = api
// endpoints/get/list.js
const api = require('../../connections/api')

module.exports = () => {
  api.get('/list', handler)
}

function handler (req, res) => {...}

In that example:

  • index.js (our entry point) triggers microboot to grab everything in the endpoints folder
  • endpoints/get/list.js is found and required
  • connections/api.js is required which set ups a single Express API
  • endpoints/get/list.js adds a GET endpoint
  • :tada:

While this is a very simple example and it could arguably be clearer having everything in a single file, the key here is extensibility. Having each endpoint separated into an individual file means adding or removing endpoints is as simple as adding or removing a file. No extra work needed.

How it works

In your main file, use microboot as it's used above, specifying a single path or multiple paths of files you want to run in the order you want them to run in. Each element in the given array is a different "stage" and files within each are sorted alphabetically to run. Here's our example:

var microboot = require('microboot')

microboot([
  'boot/databases',
  'utils/logging.js',
  'lib/endpoints/**'
]).then((arg) => {
  console.log('Boot complete!')
}).catch((err) => {
  console.error('Something went wrong:', err)
  process.exit(1)
})

In the files you choose to run, ensure they export a function that will be triggered when microboot iterates through.

You can optionally map two parameters: one that's passed through all functions, allowing you to build the object as it goes through and system and the done argument which makes the step asynchronous.

You can also return a promise to make your step asynchronous. Here are some examples:

// boot/databases/mongodb.js
// an asynchronous stage
module.exports = function mongodb (arg, done) {
  connectToMongoDb(function (connection) {
    arg.mongo = connection

    return done()
  })
}
// lib/endpoints/post/login.js
// a synchronous stage
module.exports = function post_login (arg) {
  arg.api.newAppEndpoint('post', '/login')
}
// lib/endpoints/goDb/setup.js
// a promise stage, assuming goDb.setup() returns a promise
module.exports = () => {
  return goDb.setup()
}

You're set! microboot will now first run all JS files found in the boot/databases folder (recursively) including our mongodb.js, then specifically utils/logging.js, then all JS files found in the lib/endpoints folder (recursively) including our login.js.

If you want to know more about the syntax used for specifying recursiveness and the like, take a look at glob; it's what's behind microboot's loader, microloader.

Failing to initialise

If something screws up, you should want to stop your app starting. If that's the case, you can throw an error during a step to stop things in their tracks.

For a synchronous step, just go ahead and throw:

module.exports = function my_broken_api () {
  throw new Error('Oh no! It\'s all gone wrong!')
}

For a callback step, return your error as the first argument of the callback:

module.exports = function my_broken_api (arg, done) {
  startUpApi(function (err) {
    if (err) {
      return done(err)
    }

    return done()
  })
}

For a promise step, either reject your promise or throw if you're running an async function:

module.exports = () => {
  return new Promise((resolve, reject) => {
    reject(new Error('Oh no!'))
  })
}

// or

module.exports = async () => {
  throw new Error('Oh no!')
}

Examples

Yay examples! These all assume the following directory tree, the root representing your project's current working directory.

.
├── bin
│   └── example
├── boot
│   ├── 01_logging
│   │   ├── bunyan.js
│   │   └── postal.js
│   ├── 02_amqp.js
│   └── 03_database.js
├── index.js
├── lib
│   ├── types
│   │   ├── adding.js
│   │   ├── dividing.js
│   │   ├── multiplying.js
│   │   └── subtracting.js
│   └── utils
│       ├── 404.png
│       ├── deleteFile.js
│       ├── doNothing.js
│       ├── getFile.js
│       └── hugFile.js
├── package.json
└── test
    └── test.js

7 directories, 17 files

Running everything in boot

Runs in order: bunyan.js, postal.js, amqp.js, database.js

microboot('boot')

Running everything in boot, then all utils

Runs in order: bunyan.js, postal.js, amqp.js, database.js, deleteFile.js, doNothing.js, getFile.js, hugFile.js

microboot(['boot', 'lib/utils'])

Running 01_logging after 02_amqp.js and 03_database.js

Runs in order: 02_amqp.js, 03_database.js, bunyan.js, postal.js

microboot(['boot/*', 'boot/logging'])

API reference

microboot(stages, [arg], [callback])

  • Arguments

    • stages - A single file path or array of file paths (from the CWD) from which to load Microboot's list of functions to run.
    • arg - Optional, defaults to undefined A single argument that is passed through every function run, allowing you to mutate it to build up the service as it boots. If arg is a function and no callback has been provided, arg will instead be used as the callback.
    • callback(arg) - Optional The function to run once all stages have been successfully run. Is passed the final, mutated arg.
  • Returns

    • If callback defined, undefined.
    • If callback not defined, returns a Promise resolving with arg or rejecting with an Error.

stage([arg], [callback])

  • arg - Optional The arg that's being passed through each stage run. Can be specified in the microboot call or defaults to undefined.
  • callback(err) - Optional If this is mapped to your stage function the stage will be treated as asynchronous and will require that this callback is run before moving to the next one. If there's an error, pass it back as the first parameter. If you wish to return a Promise, return one and do not specify the callback.

Debugging

If microboot doesn't seem to be behaving properly, set the DEBUG=microboot* environment variable, run your app and create a new issue with those logs.