EDGEWIRE: NODE.JS TCP LIBRARIES IN CLOUDFLARE WORKERS

Polyfill net.Socket. Use Tedious, Kysely, pg, Redis—unchanged.

TCP ADAPTER 2025.12.09
GITHUB NPM

THE PROBLEM

Problem: I needed to connect Cloudflare Workers to a GCP SQL Server instance. Hyperdrive doesn't support MSSQL. The native connect() API works, but requires rewriting all your code to use streams instead of net.Socket.

Solution: edgewire polyfills Node's net.Socket using cloudflare:sockets under the hood. Now I can use Tedious, Kysely, and any Node.js TCP library without changing a line of code.

Your Code → patchNetModule() → globalThis.net
                                    ↓
                          WorkersNetSocket class
                                    ↓
                          cloudflare:sockets API
                                    ↓
                          TCP connection to your DB

QUICK START

npm install edgewire
# or
bun add edgewire
import { patchNetModule } from 'edgewire';

// One line. Call before importing database drivers.
patchNetModule();

// Now use any Node.js TCP library unchanged
import { Connection } from 'tedious';
import { Pool } from 'pg';
import Redis from 'ioredis';

One function call. Then use any Node.js TCP library unchanged.

WHY NOT HYPERDRIVE?

Hyperdrive is great if you only need PostgreSQL. edgewire unlocks everything else.

FeatureHyperdriveedgewire
ProtocolsPostgreSQL onlyAny TCP (entire net.Socket ecosystem)
Node.js librariesPostgreSQL driversAll TCP libraries
SetupDashboard configJust code
Connection poolingManagedDurable Objects + Effect.ts
TLSBuilt-inCloudflare Tunnel

TEDIOUS + SQL SERVER

The original use case. Connect to SQL Server from Workers.

import { patchNetModule } from 'edgewire';
patchNetModule();

import { Connection, Request } from 'tedious';

export default {
  async fetch(request: Request, env: Env) {
    const connection = new Connection({
      server: env.SQL_SERVER_HOST,
      authentication: {
        type: 'default',
        options: {
          userName: env.SQL_SERVER_USER,
          password: env.SQL_SERVER_PASSWORD,
        },
      },
      options: {
        database: env.SQL_SERVER_DB,
        port: 1433,
        encrypt: false,  // Use Cloudflare Tunnel for encryption
        trustServerCertificate: true,
      },
    });

    return new Promise((resolve) => {
      connection.on('connect', (err) => {
        if (err) {
          resolve(new Response(JSON.stringify({ error: err.message }), { status: 500 }));
          return;
        }

        const request = new Request('SELECT * FROM Users WHERE Id = @id', (err, rowCount) => {
          connection.close();
          resolve(new Response(JSON.stringify({ rowCount })));
        });

        request.addParameter('id', 1);
        connection.execSql(request);
      });

      connection.connect();
    });
  },
};

KYSELY + POSTGRESQL

Type-safe queries with Kysely. Works unchanged.

import { patchNetModule } from 'edgewire';
patchNetModule();

import { Kysely, PostgresDialect } from 'kysely';
import { Pool } from 'pg';

export default {
  async fetch(request: Request, env: Env) {
    const db = new Kysely({
      dialect: new PostgresDialect({
        pool: new Pool({
          host: env.POSTGRES_HOST,
          port: 5432,
          user: env.POSTGRES_USER,
          password: env.POSTGRES_PASSWORD,
          database: env.POSTGRES_DB,
        }),
      }),
    });

    const users = await db.selectFrom('users').selectAll().execute();
    return new Response(JSON.stringify(users));
  },
};

CONNECTION POOLING

Built-in pooling via Durable Objects. Effect.ts guarantees cleanup.

import { PooledSocket } from 'edgewire';

export default {
  async fetch(request: Request, env: Env) {
    // Connection pooling via Durable Objects + Effect.ts
    const socket = new PooledSocket(env.CONNECTION_POOL, {
      host: 'db.example.com',
      port: 5432,
    });

    await socket.connect();  // Acquires from pool
    socket.write(data);
    await socket.release();  // Returns to pool (guaranteed cleanup)
  },
};

// Optional config
const socket = new PooledSocket(env.CONNECTION_POOL, options, {
  maxConnections: 20,      // Default: 20
  idleTimeout: 30000,      // Default: 30 seconds
});

POOL FEATURES

  • • Guaranteed cleanup via Effect.ts
  • • Cross-request persistence
  • • Automatic idle cleanup (30s)
  • • ~1-2ms overhead vs 50-100ms setup

HOW IT WORKS

  • • Durable Object holds pool state
  • • Connections persist across requests
  • • Shared across all Worker instances
  • • Evicted after hours of inactivity

SUPPORTED SERVICES

Anything that uses net.Socket. The integration test proves compatibility.

// Databases
SQL Server (Tedious), PostgreSQL (pg), MySQL (mysql2), 
MariaDB, MongoDB, Redis, Cassandra, Neo4j, Couchbase

// ORMs
Kysely, Drizzle, Prisma, TypeORM, MikroORM

// Message Queues
RabbitMQ, NATS, MQTT, ZeroMQ

// Other
SMTP (nodemailer), LDAP, FTP, Syslog

TLS / ENCRYPTION

edgewire provides raw TCP sockets. For encryption, use Cloudflare Tunnel:

Your Database ← cloudflared tunnel → Cloudflare Network ← Worker
     (encrypted)                                    (encrypted)

Tunnel encrypts the connection from your database to Cloudflare's edge. Worker-to-Cloudflare is already encrypted. No TLS config needed in your code.

LIVE DEMO

Proof of concept deployed at edgewire-proof.coy.workers.dev

{
  "status": "ok",
  "library": "tedious",
  "connectionType": "Connection",
  "message": "SQL Server client instantiated in Cloudflare Workers"
}

Proves the hard part: bundling + polyfill works. Actual TCP connections work the same way.

LIMITATIONS

  • Plain TCP only: No Node.js tls module. Use Cloudflare Tunnel for encryption.
  • Workers limits: 30s max request duration, memory varies by plan.
  • Design accordingly: Keep queries fast, use connection pooling.

API REFERENCE

patchNetModule()

Patches globalThis.net to use the TCP adapter. Call before importing database drivers.

PooledSocket

Connection pooling with Durable Objects + Effect.ts resource management. Methods: connect(), release(), destroy(), write().

WorkersNetSocket

Direct socket usage if not using a database driver. Implements Node.js net.Socket interface.

Get new posts by email.
Join 6 other subscribers.