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

@apostrophecms/minuscule

v1.0.0

Published

A tiny Express wrapper for fast microservice development

Readme

@apostrophecms/minuscule

A tiny Express wrapper for safe, rapid microservice development

minuscule seeks to allow developers to write API routes more safely, with less cognitive load and less chance of accidental bugs.

Specifically, minuscule allows developers to simply return a value from route functions, including async functions, and automatically encodes that as a JSON response. Similarly, minuscule allows developers to simply throw an exception from both middleware functions and route functions, whether they are async or not. minuscule also provides conveniences to create errors with the status code of the developer's choice, as well as a default 500 error for other exceptions.

Contrast this with using Express directly. Express 4 requires the developer to manually manipulate the res object, adding extra cognitive load and introducing a risk that the developr will forget to handle res, producing a hung request. Express 5 improves on this situation by catching rejected promises, but still does not allow for automatic handling of the "happy path" (the success case).

Note that minuscule middleware is different from Express middleware. While Express middleware must invoke next(), minuscule middleware just returns normally to continue execution, or throws an exception to end the request with an error.

"What about use cases like redirects, static server middleware, etc.?"

Currently minuscule does not address these edge cases, because they rarely come up in API development. However if you have a need to implement these you can just use Express middleware and routes for the purpose. We have not ruled out adding support for redirects, etc. in minuscule itself.

"Do I have to use Express 5 with minuscule?"

No, Express 4 is fine. Express 5 is also supported.

installation

npm install express
npm install @apostrophecms/minuscule

usage

const express = require('express');
const { WebError, minuscule } = require('@apostrophecms/minuscule');
const app = express();
const yup = require('yup');

// Example yup schema, see yup documentation
const projectSchema = object({
  shortName: string().required(),
  longName: string(),
  prod: boolean()
});

// Allow traditional form submission format (if you want it)
app.use(express.urlencoded({ extended: false }));

// Allow JSON submissions (recommended)
app.use(express.json());

const {
  get,
  post,
  use
} = minuscule(app);

use(expectApiKey);

// async GET API functions that just return a result

get('/projects/:projectId', expectProjectId, async req => {
  const result = await myDatabase.findOne({
    id: req.params.projectId
  });
  // returning an object -> automatic JSON response via req.res
  return result;
});

// async POST API functions with easy, safe validation

post('/projects', async req => {
  const project = await projectSchema.validate(req.body);
  await myDatabase.insertOne(project);
  // returning an object -> automatic JSON response via req.res
  return project;
});

// Global validation middleware as an async function

async function expectApiKey(req) {
  const header = req.headers.authorization;
  if (!header) {
    throw new WebError(403, 'API key required');
  }
  const matches = header.match(/^ApiKey\s+(\S.*)$/i);
  if (matches[1] !== 'some-api-key') {
    throw new WebError(403, 'Invalid API key');
  }
}

// Route-specific validation middleware as an async function

async function expectProjectId(req) {
  if (!req.params.projectId.match(/^\w+/)) {
    throw new WebError(400, 'projectId must contain only letters, digits and underscores');
  }
  req.projectId = req.params.projectId;
  // Can also use "await." If no error is thrown execution continues
}

app.listen(3000);

Other methods available

patch, put and del are available and wrap app.patch, app.post and app.delete the same way that get and post wrap app.get and app.post. del was named to avoid conflict with the delete keyword when importing.