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 🙏

© 2025 – Pkg Stats / Ryan Hefner

micro-extensions

v0.1.11

Published

Tools for building Node.js apps with Zeit's Micro

Readme

Micro extensions

Tools for building Node.js apps with Zeit's Micro

Summary

Micro defines a set of powerful primitives for building endpoints with Node.js. Micro extensions provides additional functions and helpers that extend Micro's core making it easy to weave together small applications into larger ones.

Note: we currently transpile using babel until Node.js 8 comes out then will deprecate transpiling and the need to reach into lib/ to reach things that are not exported from main.

Principles

Micro extensions also extend Micro, which is very unopinionated, with one important principle:

State mutation and other side-effects must be isolated and explicit

App example

import { configureRoutes } from 'micro-extensions/lib'

const routes = configureRoutes({
  routes: [
    { method: 'get', pattern: '/foo', handler: fooHandler },
    { method: 'get', pattern: '/foo/fuzz', handler: fuzzHandler },
    ...notFoundRoutes
  ],
  effects: { database },
  middlewares: [ logRequests ]
})

const app = createApp(routes)

micro(app).listen(2222)

Features

Everything in Micro extensions is made to work well together or separately as much as possible.

Router

A flexible router for declaratively composing request handlers, effects, middleware and application configs into applications.

  • Routers can be combined to create larger applications
  • Easy to inject effects and apply middleware to all or a subset of routes

See router.

Handlers

Request handlers accept a context object containing the familiar req and res as well as any applicable effects and/or application config. This avoids the common Node.js practice of mutating the req object to maintian context and promotes explicit declaration of a request handler dependencies. See request handlers.

Effects

Side-effects are first class concepts that materialize in Micro extensions as helpers called "effects". Effects are injected into request handlers at runtime providing well defined interfaces over state mutations and other side-effects, e.g.:

const createFooHandler = async ({ req, res, effects: { apiClient } }) => {
  return await apiClient.post('https://api.foo.com/foos', { bar: true })
}

Some common effects are provided out of the box:

  • Logger
  • Session
  • API client

Effects have access to the request context and can also be used in middleware. See effects.

Middleware

Middleware are functions that wrap response handlers. Middleware have access to a full response handler context object. See middleware.

Application config

Helpers for loading and validating application configurations. See application config.

Testing

The approach of Micro makes testing incredibly easy. Some helpers are provided to help reduce boilerplate. See testing.

Documentation

A helper app is provided for generating documentation from code. See documentation.

Plugins

Reusable sub applications can be easily created by exporting a call to configureRoutes allowing you to package routes, effects, and middleware into a set of routes for reuse in larger applications which can supply the application configuration as well as additional effects and middleware. See plugins.

Usage

Router

Routes

Routes are arrays of route declarations. Each route declares an HTTP method, a path pattern, and a handler to invoke for matching requests, e.g.:

const routes = [
  {
    method: 'get',
    pattern: '/foo/:fooId',
    handler: ({ params: { fooId } }) => ({ ok: true, fooId })
  },
  { method: 'all', pattern: '/*', handler: () => { throw createError(404, 'Not Found') } }
]

See route declarations to learn about the structure of a route.

API
configureRoutes(routes, { effects = {}, middlewares = [], config = {} })

Configure a set of routes with effects, middleware and application config.

Arguments

  • routes are an array of route definitions
  • effects are a map of effect names to objects
  • middlewares are an array of middleware functions
  • config an application configuration objects

Returns

A flattened array of route definitions

createApp(routes)

Creates a Micro-compatible handler from an array of route definitions.

Arguments

  • routes are an array of route definitions

Returns

A Micro-compatible request handler

mountAt(prefix, routes)

Adds a path prefix to a set of routes.

Arguments

  • prefix is string describing the path each route should be prefixed with.
    • Prefixes should not include parameters (TODO formalize this)
  • routes are an array of route definitions

Returns

An array of route definitions

Route declarations

A route declaration describes a URL pattern and HTTP verb combination along with a handler for matching requests.

Fields
  • method any HTTP verb or "all" to handle any verb
  • pattern a url pathname pattern
    • Named parameters are made available to request handlers via an object called params
  • handler a request handler to invoke for matching requests

Middleware

Users are encouraged to consider if desired behavior can be achieved with effects because they encourage explicit calls inside requests handler code rather than introducing the indirection that comes with middleware. With that said it's easy to define new middleware when you want to apply some behavior to one more more routes unobtrusively, e.g.:

const logMiddleware handler => ({ effects: { logger } }) => {
  logger.info('Yay, someone invoked me!')
}

Some generally useful middleware is provided

  • Request logger: provide default request logging for an app or set of routes
  • Error page renderer: renders a React component statically (server-side) with any uncaught error object

API

Arguments

  • handler request handler to be wrapped by middleware

Returns a request handler

Request handlers

Micro extensions embraces the concept of "rich request handlers" which can be found in Next.js. The only difference is that instead of the typical Node.js request handler that accepts a req as its first argument and res as its second argument it receives a single context object with req and res as top-level attributes.

Before:

const fooHandler = (req, res) => ({ ok: true })

After:

const richFooHandler = ({ req, res, query }) => ({
  ok: true,
  foo: query.foo
})

Request context fields

  • req vanilla Node.js request object
  • res vanilla Node.js response object
  • query object of key/value pairs parsed out of the URL query string
  • params object of key/value pairs parsed out of any named parameters in the requested URL
  • pathname the pathname of the requested URL
  • config application config object config object
  • effects object where the keys are the names of side-effects and the values are effects helpers

Motivation for "rich request handlers"

Adopting the convention of passing a context object into a handler counteracts the common approach across Node.js frameworks of mutating the request object as a way for different parts of the application to coordinate (e.g. express session middleware adds a session attribute to req and expects you to find it by convention in handlers). There's no great problem with this but it forces your application to depend on a "proprietary" req object and ultimately leads to indirection that makes it harder to reason about. On the other hand, by accepting a context object, libraries (like the included router) can add to the context of the request without having to rely on reading/writing to the request object. E.g. the application config is made available to all requests as a top level key:

const configurableFooHandler = ({
  req,
  res,
  config: { apiUrl }
}) => ({
  apiUrl,
  ok: true
})

Test helpers

API
suppressConsoleError()

Adds a beforeEach and afterEach to suppress all calls to console.error during test run. Used to quiet Micro tests that invoke sendError.

Plugins

There's no specific documentation for plugins because to create one you simply export a call to configureRoutes from a module.

Some plugins ship with Micro extensions:

  • Not found
  • OpenAPI docs

Inspiration