{
  "$type": "site.standard.document",
  "bskyPostRef": {
    "cid": "bafyreibvryoagxglsau7oefh6lua7utjnl4sybaqryqb2eenm6a7pvxazu",
    "uri": "at://did:plc:25rdn5elo5izoxrmtis34zuk/app.bsky.feed.post/3monhtgnmet32"
  },
  "coverImage": {
    "$type": "blob",
    "ref": {
      "$link": "bafkreiewnjkvxxfeiwuh3sz5i3crz54kehm5gr43tmz27jq3pwgpsf3z64"
    },
    "mimeType": "image/webp",
    "size": 93904
  },
  "path": "/draphy/why-thousands-of-developers-are-mass-migrating-to-pushforge-for-web-push-notifications-2eeg",
  "publishedAt": "2026-06-19T13:37:26.000Z",
  "site": "https://dev.to",
  "tags": [
    "javascript",
    "webdev",
    "typescript",
    "opensource",
    "718",
    "pushforge.draphy.org",
    "github.com/draphy/pushforge",
    "npmjs.com/package/@pushforge/builder",
    "David Raphi",
    "@pushforge",
    "@types"
  ],
  "textContent": "_\"crypto.createECDH is not a function\" - If you've seen this error, you're not alone. Here's why the web push landscape is finally changing._\n\nIf you've ever tried implementing push notifications on Cloudflare Workers, Vercel Edge, or any modern edge runtime, you've probably hit the same wall that thousands of developers face every month:\n\n\n\n    TypeError: crypto.createECDH is not a function\n\n\nOr maybe this one:\n\n\n\n    ReferenceError: https.request is not available in edge runtime\n\n\nThese errors aren't bugs in your code. They're symptoms of a fundamental problem: **the most popular web push library was built for a world that no longer exists**.\n\n##  The Uncomfortable Truth About web-push\n\nFor years, `web-push` has been the de facto standard for sending push notifications in Node.js. With 18+ million monthly downloads, it's the default choice that tutorials recommend, Stack Overflow answers reference, and ChatGPT suggests.\n\nBut here's what those recommendations don't tell you:\n\nThe Promise | The Reality\n---|---\n\"Works everywhere\" | Fails on Cloudflare Workers (#718)\n\"Simple setup\" | Requires 5+ nested dependencies\n\"Production ready\" | Node.js deprecation warnings in v24\n\"Modern JavaScript\" | Uses legacy `crypto.createECDH` API\n\n**The issue has been open since 2022.** The workarounds are fragile. The compatibility flags are band-aids. And the edge computing revolution isn't waiting.\n\n##  Enter PushForge: Built for Where You're Deploying Today\n\n**PushForge** is a zero-dependency web push library built entirely on the Web Crypto API, the same standard that runs in every modern browser, every edge runtime, and every JavaScript environment.\n\n\n\n    import { buildPushHTTPRequest } from \"@pushforge/builder\";\n\n    const { endpoint, headers, body } = await buildPushHTTPRequest({\n      privateJWK: VAPID_PRIVATE_KEY,\n      subscription: userSubscription,\n      message: {\n        payload: { title: \"Hello!\", body: \"This works everywhere.\" },\n        adminContact: \"mailto:admin@example.com\"\n      }\n    });\n\n    await fetch(endpoint, { method: \"POST\", headers, body });\n\n\nThat's it. No polyfills. No compatibility flags. No \"it works on my machine.\"\n\n##  The Comparison Developers Are Making\n\nFeature | PushForge | web-push\n---|---|---\nDependencies | **0** | 5+ (with transitive deps)\nCloudflare Workers | **Yes** | No (#718)\nVercel Edge Functions | **Yes** | No\nConvex |  **Yes** * | No\nDeno | **Yes** | Limited\nBun | **Yes** | Partial\nTypeScript | **Native** | @types package\nBundle Size | **~15KB** | ~150KB+ with deps\n\n_* Convex requires`\"use node\";` directive_\n\n##  The Numbers: Organic Growth That Speaks Volumes\n\n**72,500+ monthly downloads** and growing. Zero paid marketing. Just developers solving real problems and telling others.\n\nPushForge launched in April 2025 and has seen consistent month-over-month growth as edge computing adoption accelerates. The trajectory tells you which direction the industry is moving.\n\n##  Why PushForge Works Where web-push Fails\n\nThe difference comes down to one architectural decision: **standards vs. legacy APIs**.\n\n###  web-push approach (Node.js specific):\n\n\n    const crypto = require('crypto');\n    const https = require('https');\n    const ecdh = crypto.createECDH('prime256v1'); // Node.js only\n\n\n###  PushForge approach (Web Standards):\n\n\n    const keyPair = await crypto.subtle.generateKey(\n      { name: 'ECDH', namedCurve: 'P-256' },\n      true, ['deriveBits']\n    ); // Works everywhere\n\n\nThe Web Crypto API is:\n\n  * Available in every modern browser\n  * Supported by Cloudflare Workers, Vercel Edge, Deno, Bun\n  * Part of Node.js 20+ without flags\n  * The W3C standard for cryptographic operations\n\n\n\n**PushForge doesn't \"polyfill\" or \"shim\" its way to compatibility. It's built on the foundation that every JavaScript runtime shares.**\n\n##  Try Before You Trust: The Interactive Playground\n\nDon't take our word for it. **Test PushForge in your browser right now:**\n\n**pushforge.draphy.org**\n\nThe playground lets you:\n\n  * **Quick Test** : Enable notifications, send a test message, see it arrive\n  * **Topic Channels** : Subscribe to named topics, target specific groups\n  * **Full Customization** : Icons, images, action buttons, vibration patterns\n  * **Push Options** : Test urgency levels (battery hints), TTL, topic replacement\n  * **Cross-Browser** : Chrome, Firefox, Safari 16+, Edge, Brave\n\n\n\nThe backend? A single Cloudflare Worker using `buildPushHTTPRequest()`. Zero dependencies. The same code you'd write.\n\nSubscriptions auto-expire (5 minutes for quick test, 1 hour for topics). No accounts. No data stored. Just push notifications, working.\n\n##  Getting Started in 60 Seconds\n\n###  1. Install\n\n\n    npm install @pushforge/builder\n\n\n###  2. Generate VAPID Keys\n\n\n    npx @pushforge/builder vapid\n\n\nSave the output: public key for your frontend, private key (JWK) for your server.\n\n###  3. Subscribe Users (Frontend)\n\n\n    const registration = await navigator.serviceWorker.ready;\n    const subscription = await registration.pushManager.subscribe({\n      userVisibleOnly: true,\n      applicationServerKey: VAPID_PUBLIC_KEY\n    });\n    // Send subscription to your server\n\n\n###  4. Send Notifications (Server)\n\n\n    import { buildPushHTTPRequest } from \"@pushforge/builder\";\n\n    const { endpoint, headers, body } = await buildPushHTTPRequest({\n      privateJWK: JSON.parse(process.env.VAPID_PRIVATE_KEY),\n      subscription,\n      message: {\n        payload: {\n          title: \"New Message\",\n          body: \"You have a notification!\",\n          icon: \"/icon.png\",\n          data: { url: \"/messages\" }\n        },\n        adminContact: \"mailto:you@example.com\",\n        options: {\n          ttl: 3600,\n          urgency: \"high\"\n        }\n      }\n    });\n\n    const response = await fetch(endpoint, { method: \"POST\", headers, body });\n\n\n###  Platform Examples Ready to Copy\n\n**Cloudflare Workers:**\n\n\n\n    export default {\n      async fetch(request, env) {\n        const { endpoint, headers, body } = await buildPushHTTPRequest({\n          privateJWK: JSON.parse(env.VAPID_PRIVATE_KEY),\n          subscription: await request.json(),\n          message: {\n            payload: { title: \"Hello from the Edge!\" },\n            adminContact: \"mailto:admin@example.com\"\n          }\n        });\n        return fetch(endpoint, { method: \"POST\", headers, body });\n      }\n    };\n\n\n**Vercel Edge Functions:**\n\n\n\n    export const config = { runtime: \"edge\" };\n\n    export default async function handler(request: Request) {\n      const { endpoint, headers, body } = await buildPushHTTPRequest({\n        privateJWK: JSON.parse(process.env.VAPID_PRIVATE_KEY!),\n        subscription: await request.json(),\n        message: {\n          payload: { title: \"Edge Notification\" },\n          adminContact: \"mailto:admin@example.com\"\n        }\n      });\n      await fetch(endpoint, { method: \"POST\", headers, body });\n      return new Response(\"Sent\");\n    }\n\n\n**Deno:**\n\n\n\n    import { buildPushHTTPRequest } from \"npm:@pushforge/builder\";\n\n    const { endpoint, headers, body } = await buildPushHTTPRequest({\n      privateJWK: JSON.parse(Deno.env.get(\"VAPID_PRIVATE_KEY\")!),\n      subscription,\n      message: {\n        payload: { title: \"Hello from Deno!\" },\n        adminContact: \"mailto:admin@example.com\"\n      }\n    });\n\n    await fetch(endpoint, { method: \"POST\", headers, body });\n\n\n##  What Makes PushForge Different\n\n###  TypeScript-First, Not TypeScript-Added\n\nPushForge isn't a JavaScript library with type definitions bolted on. It's written in TypeScript from the ground up:\n\n\n\n    import type {\n      BuilderOptions,\n      PushMessage,\n      PushSubscription\n    } from \"@pushforge/builder\";\n\n\nYour IDE knows the shape of every parameter. Your compiler catches mistakes before runtime. Your code documents itself.\n\n###  Security Built In, Not Bolted On\n\nPushForge validates everything before processing:\n\n  * VAPID key structure (EC P-256 curve with required x, y, d parameters)\n  * Subscription endpoints (must be valid HTTPS URLs)\n  * p256dh keys (65-byte uncompressed P-256 point format)\n  * Auth secrets (exactly 16 bytes)\n  * Payload size (max 4KB per Web Push spec)\n  * TTL bounds (max 24 hours per VAPID spec)\n\n\n\nInvalid input fails fast with clear error messages, not cryptic failures deep in the crypto stack.\n\n##  The Roadmap: What's Coming\n\nPushForge is actively developed. Here's what's on the horizon:\n\n  * **Batching & Queuing**: Bulk notification delivery with rate limiting\n  * **Built-in Retry Logic** : Automatic error handling for push service failures\n  * **Framework Examples** : Ready-to-use templates for React, Vue, Next.js, SvelteKit\n  * **Service Worker Templates** : Drop-in notification handling code\n\n\n\nFollow development: github.com/draphy/pushforge\n\n##  The Developer Community\n\nPushForge welcomes contributions. The workflow is straightforward:\n\n  1. Open an issue describing your change\n  2. Fork the repository\n  3. Create a branch: `username/wpn-issuenumber-description`\n  4. Follow conventional commit guidelines\n  5. Submit a pull request\n\n\n\nThe codebase uses modern tooling:\n\n  * **Biome** for fast formatting and linting\n  * **Vitest** for comprehensive testing\n  * **Semantic Release** for automated versioning\n  * **GitHub Actions** for CI/CD\n\n\n\n##  Why Developers Are Switching\n\nHere's what the migration typically looks like:\n\n**Before (web-push):**\n\n\n\n    const webpush = require('web-push');\n\n    webpush.setVapidDetails(\n      'mailto:admin@example.com',\n      publicKey,\n      privateKey\n    );\n\n    // Hope you're not on Cloudflare Workers...\n    await webpush.sendNotification(subscription, payload);\n\n\n**After (PushForge):**\n\n\n\n    import { buildPushHTTPRequest } from \"@pushforge/builder\";\n\n    const { endpoint, headers, body } = await buildPushHTTPRequest({\n      privateJWK,\n      subscription,\n      message: { payload, adminContact: \"mailto:admin@example.com\" }\n    });\n\n    // Works on ANY runtime with fetch()\n    await fetch(endpoint, { method: \"POST\", headers, body });\n\n\nThe API is intentionally minimal. You get `endpoint`, `headers`, and `body`. You use `fetch()`. That's the entire interface, and it works everywhere `fetch()` works.\n\n##  The Bottom Line\n\nThe web has evolved. Serverless is the default. Edge computing is mainstream. The tools should match the territory.\n\n**PushForge is:**\n\n  * Zero dependencies (no supply chain surprises)\n  * Web standards based (no Node.js lock-in)\n  * TypeScript native (no type guessing)\n  * Edge ready (Cloudflare, Vercel, Deno, Bun - all first-class)\n  * Battle tested (70,000+ monthly downloads and growing)\n  * Open source (MIT licensed, transparent development)\n\n\n\n**web-push served its era well.** But if you're deploying to modern infrastructure, you deserve a modern foundation.\n\n##  Get Started Now\n\n\n    npm install @pushforge/builder\n\n\n  * **Documentation** : github.com/draphy/pushforge\n  * **npm** : npmjs.com/package/@pushforge/builder\n  * **Playground** : pushforge.draphy.org\n\n\n\n_PushForge is MIT licensed and open source. Created by David Raphi._\n\n_Have questions? Open an issue. Found a bug? PRs welcome. Building something cool? I'd love to hear about it._",
  "title": "Why Thousands of Developers Are Mass Migrating to PushForge for Web Push Notifications"
}