Library / SDK
vercel/micro avatar
vercel/micro

vercel/micro: a function-per-service HTTP server for containers, with no middleware and no release since 2022

Asynchronous HTTP microservices

10,633 stars454 forksTypeScriptMIT

At a glance

What is it?
Micro turns one exported async function into an HTTP service, parses bodies only when you ask, and sends whatever you return. It is scoped to production containers, it is explicitly not meant for serverless or for local development, and its last tagged release is v10.0.1 from November 2022.
Who is it for?
Micro suits a developer who wants a single-purpose HTTP endpoint inside a container, written as one async function, with body parsing and status codes handled by four small helpers. It does not suit Vercel serverless functions, which its own README says gain nothing from it, or teams that expect middleware chains.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 6 days ago.
What is it written in?
Mainly TypeScript, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on November 14, 2026, and from our analysis. They are not legal advice.

From daily snapshots

Star history

  • vercel/micro10,633 ★ · +5
Star history
Repository2026-09-222026-11-14
vercel/micro10.6k10.6k

5 Stars in the last 53 days, measured from the daily GitHub snapshot. Compare with other projects

Editorial analysis

Micro targets containers and tells Vercel users they do not need it

Micro comes from Vercel, yet the most direct warning in its README is aimed at Vercel's own customers. The disclaimer says Micro was created for use within containers and is not intended for use in serverless environments. For anyone deploying to Vercel, it states there is no requirement to use Micro because the benefits it provides do not apply to the platform, and that utilities such as json are already available as Serverless Function helpers in the Node.js runtime.

That shapes who should pick it up. Micro makes sense when you control a long-running process: you build an image, the container starts a Node.js server, and the server listens on a port. If your code instead runs as a function the platform invokes per request, Micro adds a layer whose job the platform already does, and the request and response helpers you would want are provided by the runtime. Put simply, Micro is a small server for a container, not an adapter for a functions platform, and choosing it for the second case gives you a dependency with no payoff.

Production only: local development is handed to micro-dev

The installation section opens with a second restriction: Micro is only meant to be used in production. For development, the README sends you to a separate package, micro-dev, described as a tool belt tailored for developing microservices. The production install itself is a single dependency:

bash
npm install --save micro

The consequence is that a Micro project has two runtimes to keep in step. The micro binary is the thing you ship, and it carries none of the development conveniences, so anyone on the team who runs npm start locally gets the bare production server. If you want a different experience while editing code, you add micro-dev and keep its version aligned with micro yourself. Teams used to frameworks where one command covers both modes should budget for that split, and should check that micro-dev still works with the micro version they pin, since the two are separate packages with separate release histories.

A service is one exported function, and its return value becomes the response

The programming model is the main reason people pick Micro. You create index.js and export a function that receives the standard Node.js http.IncomingMessage and http.ServerResponse objects:

js
module.exports = (req, res) => {
  res.end('Welcome to Micro');
};

Micro also handles return values, so the same service can be written as:

js
module.exports = () => 'Welcome to Micro';

Returning a value is equivalent to calling send(res, 200, value), so a plain return always produces a 200. The function can be async, and Micro awaits it, which is how the README pitches the project: designed for async and await. To run it, package.json needs main pointing at the file and a start script that calls micro:

json
{
  "main": "index.js",
  "scripts": {
    "start": "micro"
  }
}

After npm start, the service answers on http://localhost:3000. One file, one function, one port: that is the whole unit Micro is built around, and it is why the README calls it oriented for single purpose modules.

The micro CLI finds one entry point, and a single -l replaces the default address

The command line has three forms: micro --help, micro --version (also -v) and micro [-l listen_uri [-l ...]] [entry_point.js]. With no arguments, Micro listens on 0.0.0.0:3000. It resolves the entry point by looking first at the main property in package.json and then at index.js.

The -l flag has a rule that catches people out: specifying a single --listen argument overwrites the default rather than adding to it. If you pass one -l for a custom address, port 3000 on 0.0.0.0 is no longer bound. The flag can be repeated to listen on more than one address. For containers that inject the port through the environment, the README gives:

code
micro -l tcp://0.0.0.0:$PORT

and a variant with a fallback:

code
micro -l tcp://0.0.0.0:${PORT-3000}

The ${PORT-3000} expansion falls back to 3000 when PORT is not defined, and the README notes that this only works in Bash. If your container's start command does not run under Bash, do not rely on that fallback; set the port another way.

Body parsing is opt-in, capped at 1mb, and fails with 413 or 400

Micro does not parse request bodies unless you call one of its helpers; the README lists even JSON parsing as opt-in. Three async functions cover it, each with the same signature and defaults:

js
const { buffer, text, json } = require('micro');

buffer(req, { limit = '1mb', encoding = 'utf8' }) returns the raw bytes, text returns a string, and json returns the parsed object. All three buffer the incoming body before returning, and they cache the raw body the first time, so calling json after text in the same handler does not try to read a stream that has already been consumed.

The limits are where failures appear. limit is the maximum amount of data aggregated before parsing, given either as a number of bytes or as a string like '1mb'. If a request exceeds it, the helper throws an Error with statusCode set to 413. If JSON parsing fails, it throws an Error with statusCode 400. A service that accepts uploads or large JSON documents will reject them at 1mb until you pass a larger limit, and because the whole body is buffered in memory first, raising the limit raises per-request memory use as well. For urlencoded forms, the examples directory carries urlencoded-body-parsing.

send() chooses the encoding from the data, and stream errors are your job

When you need a status other than 200, you call send(res, statusCode, data = null) instead of returning. statusCode must always be supplied. Micro sets Content-Type and Content-Length automatically based on what data is:

- a Stream is piped as an octet-stream - a Buffer is written as an octet-stream - an object is serialized as JSON - a string is written as-is

Two edges matter in production. First, if JSON serialization fails, for example because the object has a cyclical reference, Micro throws a 400 error. That is a server-side bug reported to the client as a bad request, so a 400 in your logs is not always the caller's fault. Second, for streams the README states plainly that handling the error event is your responsibility, and suggests logging the error and aborting the response is usually enough. If you pipe a file or an upstream response through send and forget that handler, a failing source stream is left for your own code to deal with, because Micro does not attach one for you.

The README's own example sends a 400 with an object body:

js
const { send } = require('micro');

module.exports = async (req, res) => {
  const statusCode = 400;
  const data = { error: 'Custom error message' };

  send(res, statusCode, data);
};

No middleware means every concern is an explicit function call

Micro lists no middleware as a feature: modules declare all their dependencies instead of registering into a shared pipeline. Authentication, CORS, routing, logging and rate limiting each become either a function you wrap around your handler or a separate package, and the README points to the awesome-micro list for those. The README also claims the whole project is about 260 lines of code and that the package with all dependencies weighs less than a megabyte.

The trade is visibility for convenience. Reading a Micro service tells you exactly what runs on each request, because every step is an import and a call. But the moment a service needs several routes or shared cross-cutting behavior, you are assembling that structure yourself. When you need the server inside an existing Node.js program, serve(fn) returns a (req, res) => void handler you can pass to http.Server:

js
const http = require('http');
const sleep = require('then-sleep');
const { serve } = require('micro');

const server = new http.Server(
  serve(async (req, res) => {
    await sleep(500);
    return 'Hello world';
  }),
);

server.listen(3000);

The examples directory shows how far the single-function model is pushed: external-api-call, json-body-parsing, urlencoded-body-parsing, socket.io-chat-app, with-graphql-request and with-https.

The last tagged release is v10.0.1 from November 2022

The release history is short and old: 9.4.1 on 2022-07-31, v10.0.0 on 2022-11-25 and v10.0.1 on 2022-11-26. The repository is not archived and its last push was on 2026-05-21, but no tagged release has followed v10.0.1. If you install micro from npm today, you are installing code from the end of 2022 unless something has been published outside the GitHub release list.

The repository itself is a monorepo. The root package.json is private, declares pnpm 10.33.0 as its package manager, and defines workspaces for packages/* and test. Publishing runs through lerna 3.4.0, with a publish-canary script that cuts a canary prerelease and a publish-stable script for normal versions; tests run with pnpm --dir test run test. An errors/ directory sits at the top level alongside the examples.

For a dependency this small, an old release is not automatically a problem, since there is little surface to break. The risk is compatibility: before standardizing on Micro, run your own handler against the Node.js version your containers use, and decide whether you are willing to patch or fork it if a newer runtime breaks something.

Editorial conclusion

Micro suits a developer who wants a single-purpose HTTP endpoint inside a container, written as one async function, with body parsing and status codes handled by four small helpers. It does not suit Vercel serverless functions, which its own README says gain nothing from it, or teams that expect middleware chains. Before adopting it, check that v10.0.1 from 2022-11-26 still matches your Node.js version and plan for micro-dev or another tool during development.

Frequently asked questions

Is there a Micro alternative for Vercel deployments?

Micro's README says Vercel users do not need Micro, because its benefits do not apply to the platform. Helpers such as json are available as Serverless Function helpers in Vercel's Node.js runtime.

Which port does vercel/micro listen on by default?

Micro listens on 0.0.0.0:3000 by default. Passing a single -l or --listen argument replaces that default instead of adding to it.

What happens when a request body is too large for Micro?

Micro's buffer, text and json helpers default to a 1mb limit. A body over the limit throws an Error with statusCode 413, and invalid JSON throws one with statusCode 400.

Should I use Micro during development?

No. Micro's README says it is only meant for production and points developers to micro-dev for local development.

Official sources

  1. Issues
  2. License: MIT
  3. README
  4. Releases
  5. vercel/micro on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/vercel-micro.svg)](https://hysenlabs.com/projects/vercel-micro)

Star history chart for your README

markdown
[![Star history](https://hysenlabs.com/chart/vercel-micro.svg)](https://hysenlabs.com/projects/vercel-micro)

Was this useful?