Skip to main content

Best npm Packages for Edge Runtimes in 2026

·PkgPulse Team

Not every npm package works on edge runtimes. Edge environments use the Fetch API instead of Node.js's http module, lack access to the filesystem, and have CPU time limits (10-50ms on Cloudflare Workers). The packages that work best are those built on Web Standards — and in 2026, that list has grown substantially as npm package authors have added edge compatibility.

TL;DR

The edge runtime npm ecosystem in 2026 centers on a few key categories: routing (Hono — 2M downloads, works on 9+ runtimes), storage (Cloudflare's D1, KV, R2 client libraries), validation (Zod works edge-natively, so does Valibot), and utilities (ioredis has alternatives, most date libraries work). Avoid packages that require fs, child_process, net, or Node.js-only streams — they'll fail at the edge.

Key Takeaways

  • Hono: 2M weekly downloads, <14 kB, the standard router for edge runtimes
  • wrangler: Cloudflare's CLI + local emulation for D1, KV, R2, Durable Objects
  • @cloudflare/workers-types: TypeScript types for Workers APIs (KV, D1, R2, etc.)
  • @cloudflare/vitest-pool-workers: Run tests inside the actual Workers runtime
  • Zod: Fully edge-compatible (no Node.js dependencies)
  • Packages to avoid: express, axios (use native fetch), fs-extra, bcrypt (use SubtleCrypto)
  • Web Crypto API: Use crypto.subtle instead of Node.js crypto module

The Edge Runtime Constraint

Edge runtimes implement the WinterCG spec — a subset of Web APIs without Node.js-specific APIs:

// Available in edge runtimes:
fetch()          // HTTP requests
Request          // Web Fetch API
Response         // Web Fetch API
URL              // URL parsing
URLSearchParams  // Query string
Headers          // HTTP headers
crypto.subtle    // Web Crypto (NOT Node.js crypto)
TextEncoder      // Text encoding
TextDecoder      // Text decoding
ReadableStream   // Streaming

// NOT available in edge runtimes:
fs               // No filesystem
child_process    // No process spawning
net              // No raw TCP
node:crypto      // No Node.js crypto (use crypto.subtle)
Buffer           // Limited (polyfill available in Workers)

Routing: Hono

Package: hono Weekly downloads: 2M GitHub stars: 23K

Hono is the standard framework for edge runtimes. It's built on Web Standards, has zero platform-specific dependencies, and works on Cloudflare Workers, Deno Deploy, Vercel Edge, AWS Lambda, Bun, and Node.js.

npm install hono
// Works identically on Cloudflare Workers, Deno Deploy, and Node.js
import { Hono } from 'hono';
import { cors } from 'hono/cors';
import { logger } from 'hono/logger';
import { validator } from 'hono/validator';
import { zValidator } from '@hono/zod-validator';
import { z } from 'zod';

const app = new Hono<{ Bindings: Env }>();

app.use('*', cors());
app.use('*', logger());

const userSchema = z.object({
  name: z.string().min(2),
  email: z.string().email(),
});

app.post('/api/users',
  zValidator('json', userSchema),
  async (c) => {
    const data = c.req.valid('json');
    const { meta } = await c.env.DB
      .prepare('INSERT INTO users (name, email) VALUES (?, ?)')
      .bind(data.name, data.email)
      .run();
    return c.json({ id: meta.last_row_id }, 201);
  }
);

export default app;

Cloudflare Platform Packages

wrangler — The Essential CLI

npm install -D wrangler
# Local development with D1, KV, R2 emulation
wrangler dev

# Deploy to Cloudflare Workers
wrangler deploy

# Manage KV namespaces
wrangler kv namespace create CACHE
wrangler kv key put --binding=CACHE "key" "value"

# Create D1 database
wrangler d1 create my-database
wrangler d1 execute my-database --file=schema.sql

# Tail real-time logs from production
wrangler tail

# Generate TypeScript types from wrangler.toml
wrangler types

@cloudflare/workers-types — TypeScript Types

npm install -D @cloudflare/workers-types
// tsconfig.json
{
  "compilerOptions": {
    "types": ["@cloudflare/workers-types"]
  }
}

// Or use wrangler types (preferred in 2026):
// Creates worker-configuration.d.ts with your specific bindings
// Now TypeScript knows about all Workers APIs:
async function handler(request: Request, env: Env): Promise<Response> {
  // env.KV is typed as KVNamespace
  const value = await env.KV.get('my-key');

  // env.DB is typed as D1Database
  const users = await env.DB.prepare('SELECT * FROM users').all();

  // env.BUCKET is typed as R2Bucket
  const object = await env.BUCKET.get('image.png');

  return Response.json(users.results);
}

@cloudflare/vitest-pool-workers — Test in the Real Runtime

npm install -D @cloudflare/vitest-pool-workers vitest
// vitest.config.ts
import { defineWorkersConfig } from '@cloudflare/vitest-pool-workers/config';

export default defineWorkersConfig({
  test: {
    poolOptions: {
      workers: {
        wrangler: { configPath: './wrangler.toml' },
      },
    },
  },
});
// api.test.ts — runs inside the actual Workers runtime
import { SELF } from 'cloudflare:test';
import { describe, it, expect } from 'vitest';

describe('API', () => {
  it('returns users', async () => {
    // SELF is your Worker — real D1, KV, R2 bindings
    const response = await SELF.fetch('http://localhost/api/users');
    expect(response.status).toBe(200);

    const users = await response.json();
    expect(Array.isArray(users)).toBe(true);
  });
});

Tests run in the actual Workers runtime with real bindings — not a Node.js simulation.

@cloudflare/d1 and Database Patterns

// D1 (SQLite at the edge) — no separate client package needed
// Access via the env binding:

interface Env {
  DB: D1Database;
}

export default {
  async fetch(request: Request, env: Env) {
    // Query
    const users = await env.DB
      .prepare('SELECT * FROM users WHERE active = ?')
      .bind(true)
      .all();

    // Insert
    const { meta } = await env.DB
      .prepare('INSERT INTO users (name, email) VALUES (?, ?)')
      .bind('Alice', 'alice@example.com')
      .run();

    // Batch queries
    const results = await env.DB.batch([
      env.DB.prepare('INSERT INTO users (name) VALUES (?)').bind('Alice'),
      env.DB.prepare('INSERT INTO users (name) VALUES (?)').bind('Bob'),
    ]);

    return Response.json({ users: users.results });
  },
};

Edge-Compatible Libraries

Validation: Zod and Valibot

npm install zod
# or:
npm install valibot  # Smaller bundle, fully edge-compatible

Both work natively in edge runtimes — no Node.js dependencies.

HTTP Client: Native Fetch

// DON'T use axios in edge runtimes (node http module dependency)
// import axios from 'axios';  // May fail on Workers

// DO use native fetch — available in all edge runtimes:
const response = await fetch('https://api.example.com/data', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ key: 'value' }),
});
const data = await response.json();

Cryptography: Web Crypto API

// DON'T use Node.js crypto:
// import crypto from 'crypto';  // Not available on edge

// DO use Web Crypto:
async function hashPassword(password: string): Promise<string> {
  const encoder = new TextEncoder();
  const data = encoder.encode(password);
  const hash = await crypto.subtle.digest('SHA-256', data);
  return btoa(String.fromCharCode(...new Uint8Array(hash)));
}

// JWT verification with Web Crypto:
async function verifyJWT(token: string, secret: string): Promise<boolean> {
  const key = await crypto.subtle.importKey(
    'raw',
    new TextEncoder().encode(secret),
    { name: 'HMAC', hash: 'SHA-256' },
    false,
    ['verify']
  );
  // ... verify signature
}

ORM: Drizzle with D1

npm install drizzle-orm
npm install -D drizzle-kit
// schema.ts
import { sqliteTable, text, integer } from 'drizzle-orm/sqlite-core';

export const users = sqliteTable('users', {
  id: integer('id').primaryKey({ autoIncrement: true }),
  name: text('name').notNull(),
  email: text('email').notNull().unique(),
});
// index.ts
import { drizzle } from 'drizzle-orm/d1';
import { users } from './schema';
import { eq } from 'drizzle-orm';

export default {
  async fetch(request: Request, env: Env) {
    const db = drizzle(env.DB);

    const allUsers = await db.select().from(users);
    const user = await db.select()
      .from(users)
      .where(eq(users.email, 'alice@example.com'))
      .get();

    return Response.json(allUsers);
  }
};

Date Libraries

npm install date-fns  # Fully edge-compatible
// date-fns works in edge runtimes (no Node.js dependencies)
import { format, addDays, differenceInDays } from 'date-fns';

const formatted = format(new Date(), 'yyyy-MM-dd');

What NOT to Use in Edge Runtimes

// ❌ Node.js http module — use fetch instead
import http from 'http';

// ❌ Express — not edge-compatible
import express from 'express';

// ❌ Axios — depends on Node.js http (though newer versions have fetch mode)
import axios from 'axios';

// ❌ bcrypt — uses native bindings
import bcrypt from 'bcrypt';
// ✅ Use: bcryptjs (pure JS) or Web Crypto

// ❌ sharp — requires native bindings
import sharp from 'sharp';
// ✅ Use: Cloudflare Images API or a separate service

// ❌ node:crypto (import via node: protocol)
import crypto from 'node:crypto';
// ✅ Use: crypto.subtle (Web Crypto API)

// ❌ fs — no filesystem in edge runtimes
import fs from 'fs';
// ✅ Use: R2 for file storage, KV for small data

Edge Compatibility Checklist

Before using any npm package on an edge runtime:

# Check if package has "edge" in keywords or README
# Check for Node.js-specific imports in source

# Common red flags:
# - import { createServer } from 'http'
# - import { readFile } from 'fs'
# - import { exec } from 'child_process'
# - Mentions "Node.js required" in README

# Good signs:
# - "Works with Cloudflare Workers" in README
# - Uses only Web Standards APIs
# - Zero dependencies or all web-standard deps
# - "edge-runtime" package.json exports condition

The 2026 Edge Stack

// wrangler.toml
name = "my-api"
main = "src/index.ts"
compatibility_date = "2024-01-01"
compatibility_flags = ["nodejs_compat"]  // Enable Node.js compat

[[d1_databases]]
binding = "DB"
database_name = "my-database"
database_id = "..."

[[kv_namespaces]]
binding = "CACHE"
id = "..."

[[r2_buckets]]
binding = "BUCKET"
bucket_name = "my-assets"
// package.json
{
  "dependencies": {
    "hono": "^4.x",         // Routing
    "drizzle-orm": "^0.x",  // ORM for D1
    "zod": "^3.x",          // Validation
    "@hono/zod-validator": "^0.x"  // Hono + Zod integration
  },
  "devDependencies": {
    "wrangler": "^3.x",
    "@cloudflare/workers-types": "^4.x",
    "@cloudflare/vitest-pool-workers": "^0.x",
    "drizzle-kit": "^0.x",
    "vitest": "^2.x"
  }
}

Compare edge-compatible package downloads on PkgPulse.

Comments

Stay Updated

Get the latest package insights, npm trends, and tooling tips delivered to your inbox.