@vercel/edge vs @vercel/node vs express vs hapi vs koa vs micro vs polka vs serverless-http
Server-Side JavaScript Runtimes and Frameworks for Modern Web Applications
@vercel/edge@vercel/nodeexpresshapikoamicropolkaserverless-httpSimilar Packages:

Server-Side JavaScript Runtimes and Frameworks for Modern Web Applications

These packages represent different approaches to building server-side JavaScript applications, from traditional Node.js frameworks to modern edge computing solutions. express, hapi, koa, micro, and polka are Node.js web frameworks with varying levels of abstraction and opinionation. @vercel/edge and @vercel/node are Vercel-specific runtime adapters for deploying functions to edge networks or Node.js environments. serverless-http acts as a bridge between traditional Node.js frameworks and serverless platforms like AWS Lambda. Each serves different deployment targets and architectural needs.

Npm Package Weekly Downloads Trend

3 Years

Github Stars Ranking

Stat Detail

Package
Downloads
Stars
Size
Issues
Publish
License
@vercel/edge015,52648.6 kB67213 days agoApache-2.0
@vercel/node015,5264.34 MB6727 hours agoApache-2.0
express069,04975.4 kB2116 months agoMIT
hapi014,786-627 years agoBSD-3-Clause
koa035,71565 kB372 days agoMIT
micro010,61542.1 kB8-MIT
polka05,589-317 years agoMIT
serverless-http01,78239.7 kB599 months agoMIT

Server-Side JavaScript Runtimes and Frameworks: A Technical Deep-Dive

Building server-side JavaScript applications today means choosing between traditional Node.js frameworks, modern edge computing solutions, and serverless adapters. Each package in this comparison serves different deployment targets and architectural needs. Let's examine how they handle real-world development scenarios.

πŸ—οΈ Core Architecture: Framework vs Runtime Adapter

The packages fall into two distinct categories. express, hapi, koa, micro, and polka are full web frameworks that run on Node.js servers. @vercel/edge, @vercel/node, and serverless-http are runtime adapters that help existing code run on specific platforms.

express uses a request-response middleware pattern where each middleware can modify the request or response before passing control to the next.

// express: Middleware chain
import express from 'express';
const app = express();

app.use((req, res, next) => {
  console.log('Time:', Date.now());
  next();
});

app.get('/user/:id', (req, res) => {
  res.json({ id: req.params.id });
});

app.listen(3000);

hapi uses a configuration-driven approach with plugins and built-in validation.

// hapi: Plugin-based architecture
import { Hapi } from '@hapi/hapi';

const server = Hapi.server({ port: 3000 });

server.route({
  method: 'GET',
  path: '/user/{id}',
  handler: (request, h) => {
    return { id: request.params.id };
  }
});

await server.start();

koa uses async functions and middleware composition without callbacks.

// koa: Async middleware composition
import Koa from 'koa';
const app = new Koa();

app.use(async (ctx, next) => {
  console.log('Time:', Date.now());
  await next();
});

app.use(async ctx => {
  ctx.body = { id: ctx.params.id };
});

app.listen(3000);

micro treats each export as a single request handler (now archived β€” see deprecation note below).

// micro: Single function per endpoint
import { send, json } from 'micro';

export default async (req, res) => {
  const data = await someAsyncOperation();
  send(res, 200, json({ data }));
};

polka offers Express-like routing with minimal overhead.

// polka: Lightweight routing
import polka from 'polka';
const app = polka();

app.use((req, res, next) => {
  console.log('Time:', Date.now());
  next();
});

app.get('/user/:id', (req, res) => {
  res.end(JSON.stringify({ id: req.params.id }));
});

app.listen(3000);

@vercel/edge runs code on Vercel's edge network using Web Standards API.

// @vercel/edge: Edge function
import { EdgeRequest } from '@vercel/edge';

export const config = { runtime: 'edge' };

export default async function handler(request) {
  const url = new URL(request.url);
  return new Response(
    JSON.stringify({ path: url.pathname }),
    { headers: { 'content-type': 'application/json' } }
  );
}

@vercel/node runs Node.js code in Vercel's serverless environment.

// @vercel/node: Serverless Node.js function
export default async function handler(request, response) {
  response.status(200).json({ 
    path: request.url,
    runtime: 'nodejs'
  });
}

serverless-http wraps existing frameworks for serverless platforms.

// serverless-http: Framework adapter for Lambda
import serverless from 'serverless-http';
import express from 'express';

const app = express();
app.get('/user/:id', (req, res) => {
  res.json({ id: req.params.id });
});

export const handler = serverless(app);
// Deploy handler to AWS Lambda

⚑ Performance and Bundle Size Considerations

Bundle size matters for serverless functions where cold start time impacts user experience.

express includes many features by default, resulting in larger bundles.

// express: Full-featured but larger
import express from 'express';
// Includes body parsing, routing, middleware system
const app = express();
app.use(express.json()); // Built-in body parser

polka keeps the core minimal, requiring explicit middleware additions.

// polka: Minimal core
import polka from 'polka';
import bodyParser from 'body-parser';
const app = polka();
app.use(bodyParser.json()); // Explicit middleware

@vercel/edge has the smallest footprint but limited API surface.

// @vercel/edge: Web Standards only
export default async function handler(request) {
  // No Node.js APIs like fs, path, etc.
  // Only Web APIs: fetch, URL, Headers, Response
  return new Response('Hello');
}

koa ships with minimal core, requiring middleware for common tasks.

// koa: Minimal core
import Koa from 'koa';
import bodyParser from 'koa-bodyparser';
const app = new Koa();
app.use(bodyParser()); // External middleware needed

πŸ”„ Middleware and Request Handling Patterns

Each framework handles middleware differently, affecting code organization and debugging.

express passes control with next() callback.

// express: Callback-based middleware
app.use((req, res, next) => {
  if (!req.headers.auth) {
    return res.status(401).send('Unauthorized');
  }
  next(); // Continue to next middleware
});

koa uses async/await with next() as a promise.

// koa: Async middleware
app.use(async (ctx, next) => {
  if (!ctx.headers.auth) {
    ctx.status = 401;
    return; // No need to call next()
  }
  await next(); // Wait for downstream middleware
});

hapi uses lifecycle extensions with explicit phases.

// hapi: Lifecycle extensions
server.ext('onRequest', (request, h) => {
  if (!request.headers.auth) {
    return h.response('Unauthorized').code(401);
  }
  return h.continue; // Explicit continue signal
});

polka follows Express pattern with simpler implementation.

// polka: Express-like middleware
app.use((req, res, next) => {
  if (!req.headers.auth) {
    return res.end('Unauthorized');
  }
  next();
});

@vercel/edge uses standard Web API middleware patterns.

// @vercel/edge: Middleware function
export function middleware(request) {
  if (!request.headers.get('auth')) {
    return new Response('Unauthorized', { status: 401 });
  }
  // Return undefined to continue or Response to short-circuit
}

serverless-http passes through framework middleware unchanged.

// serverless-http: No middleware changes needed
// Your Express/Koa middleware works as-is
const handler = serverless(app); // app contains all middleware

🌍 Deployment Targets and Runtime Constraints

Where you deploy determines which packages make sense.

@vercel/edge deploys to edge locations globally with ~100ms cold starts.

// @vercel/edge: vercel.json configuration
{
  "functions": {
    "api/*.js": {
      "runtime": "@vercel/edge"
    }
  }
}

@vercel/node deploys to Vercel serverless with Node.js runtime.

// @vercel/node: vercel.json configuration
{
  "functions": {
    "api/*.js": {
      "runtime": "@vercel/node"
    }
  }
}

express, koa, hapi, polka deploy to traditional Node.js servers or containers.

// Traditional deployment (Docker, VM, etc.)
import express from 'express';
const app = express();
app.listen(process.env.PORT || 3000);
// Deploy to: AWS EC2, DigitalOcean, Heroku, Docker containers

serverless-http deploys to AWS Lambda, Azure Functions, Google Cloud Functions.

// serverless-http: AWS Lambda deployment
exports.handler = serverless(app);
// Deploy via: AWS SAM, Serverless Framework, Terraform

micro was designed for Now/v1 serverless (now archived).

// micro: Historical usage (no longer recommended)
module.exports = async (req, res) => {
  // Deployed to Now v1 serverless
};

πŸ”’ Error Handling and Response Management

Error handling varies significantly between frameworks.

express uses error-handling middleware with 4 parameters.

// express: Error middleware
app.use((err, req, res, next) => {
  console.error(err.stack);
  res.status(500).json({ error: 'Something broke!' });
});

koa uses try/catch in async middleware.

// koa: Try/catch error handling
app.use(async (ctx, next) => {
  try {
    await next();
  } catch (err) {
    ctx.status = 500;
    ctx.body = { error: err.message };
  }
});

hapi has built-in error handling with response toolkit.

// hapi: Built-in error handling
server.route({
  method: 'GET',
  path: '/user',
  handler: async (request, h) => {
    try {
      return await getUser();
    } catch (err) {
      return h.response(err).code(500);
    }
  }
});

polka uses Express-style error handling.

// polka: Error middleware
app.use((err, req, res, next) => {
  res.statusCode = 500;
  res.end(err.message);
});

@vercel/edge returns Response objects with error status.

// @vercel/edge: Error responses
export default async function handler(request) {
  try {
    const data = await fetchData();
    return Response.json(data);
  } catch (err) {
    return Response.json({ error: err.message }, { status: 500 });
  }
}

@vercel/node uses standard Node.js response objects.

// @vercel/node: Error handling
export default async function handler(request, response) {
  try {
    response.status(200).json({ data: 'ok' });
  } catch (err) {
    response.status(500).json({ error: err.message });
  }
}

serverless-http passes framework errors through unchanged.

// serverless-http: Framework errors propagate
// Your Express/Koa error handlers work as-is in Lambda
const handler = serverless(app);

⚠️ Deprecation and Maintenance Status

micro is officially archived by its maintainer. The repository shows deprecation notices and recommends alternatives.

// micro: DO NOT USE for new projects
// Archived status: https://github.com/vercel/micro
// Recommended alternatives: polka, native http module, or framework of choice

All other packages maintain active development as of current npm listings. express remains the most widely adopted with stable LTS releases. @vercel/edge and @vercel/node receive updates aligned with Vercel platform changes.

πŸ“Š Feature Comparison Summary

Featureexpresshapikoapolka@vercel/edge@vercel/nodeserverless-http
RuntimeNode.jsNode.jsNode.jsNode.jsEdgeNode.jsAny (adapter)
MiddlewareCallbackLifecycleAsyncCallbackWeb APINode.jsPass-through
Bundle SizeLargeLargeMediumSmallMinimalMedium+Framework
Learning CurveLowMediumMediumLowLowLowLow
Serverless ReadyWith adapterWith adapterWith adapterYesNativeNativeYes
Edge CompatibleNoNoNoNoYesNoNo
MaintenanceActiveActiveActiveActiveActiveActiveActive

πŸ’‘ Practical Selection Guide

Choose express when:

  • You need the largest middleware ecosystem
  • Your team has Express experience
  • You're deploying to traditional servers or containers
  • Bundle size isn't a critical concern

Choose polka when:

  • You want Express-like DX with smaller footprint
  • You're building serverless functions
  • Performance and cold starts matter
  • You prefer minimal framework overhead

Choose koa when:

  • You want modern async/await patterns
  • You're comfortable building custom abstractions
  • You need cleaner middleware composition than Express
  • Your team values code clarity over convention

Choose hapi when:

  • You need enterprise-grade configuration
  • Built-in validation is important
  • You prefer convention over flexibility
  • Long-term stability matters more than trends

Choose @vercel/edge when:

  • You deploy to Vercel
  • Low latency is critical (global users)
  • Your logic is simple (auth, redirects, headers)
  • You can work within Web Standards API limits

Choose @vercel/node when:

  • You deploy to Vercel
  • You need full Node.js API access
  • You're migrating existing Express/Koa apps
  • Cold start time is acceptable for your use case

Choose serverless-http when:

  • You deploy to AWS Lambda or similar
  • You have existing Express/Koa/hapi code
  • You want minimal refactoring for serverless
  • You need framework features in serverless environment

Avoid micro for new projects:

  • Officially archived by maintainer
  • Limited to single-function endpoints
  • Better alternatives exist (polka, native http)
  • No active maintenance or security updates

🎯 Final Thoughts

The right choice depends on your deployment target and team preferences. Traditional frameworks (express, koa, hapi, polka) work anywhere Node.js runs. Platform-specific adapters (@vercel/edge, @vercel/node, serverless-http) optimize for specific hosting environments.

For new projects in 2024, consider starting with polka for APIs (small, fast, Express-like) or @vercel/edge if you're on Vercel and need edge performance. Use serverless-http only when migrating existing applications to serverless without rewriting business logic.

Remember: framework choice is less important than understanding your deployment constraints, performance requirements, and team expertise. All active packages listed here can build production-ready applications when used appropriately.

How to Choose: @vercel/edge vs @vercel/node vs express vs hapi vs koa vs micro vs polka vs serverless-http

  • @vercel/edge:

    Choose @vercel/edge when deploying to Vercel's Edge Functions for low-latency, globally distributed applications. Ideal for middleware, authentication checks, and simple request/response transformations that benefit from edge caching. Not suitable for heavy computation or Node.js-specific APIs.

  • @vercel/node:

    Choose @vercel/node when you need full Node.js compatibility within Vercel's serverless function environment. Best for existing Express/Koa applications being migrated to Vercel without major refactoring. Offers more APIs than edge but slower cold starts than edge functions.

  • express:

    Choose express for traditional Node.js applications requiring mature middleware ecosystem and extensive community support. Ideal for APIs, SSR applications, and teams familiar with its request/response pattern. Consider alternatives if bundle size or minimalism is a priority.

  • hapi:

    Choose hapi when building enterprise-grade APIs with strong configuration-driven architecture and built-in input validation. Suitable for teams valuing convention over flexibility and needing robust plugin systems. Less common in modern startups but stable for long-term projects.

  • koa:

    Choose koa when you want modern async/await support with minimal framework overhead. Built by Express creators, it offers cleaner middleware composition without callback hell. Best for teams comfortable building custom abstractions on top of a lightweight core.

  • micro:

    Choose micro for ultra-minimal HTTP microservices where each function handles a single endpoint. Now archived by maintainer β€” evaluate alternatives like polka or native Node.js http module for new projects. Was ideal for simple serverless functions.

  • polka:

    Choose polka when you need Express-like routing with significantly smaller footprint and faster performance. Great for serverless functions, APIs, and projects where bundle size matters. Less middleware ecosystem than Express but growing community support.

  • serverless-http:

    Choose serverless-http when migrating existing Express/Koa/hapi applications to AWS Lambda or similar serverless platforms without rewriting business logic. Acts as a translation layer between Node.js frameworks and serverless event formats.

README for @vercel/edge

@vercel/edge

⚠️ Deprecation Notice: This package has been unified in @vercel/functions. Use it instead!

A set of utilities to help you deploy any framework on the Edge using Vercel. Please follow the documentation for examples and usage.