EDGEWIRE: NODE.JS TCP LIBRARIES IN CLOUDFLARE WORKERS
Polyfill net.Socket. Use Tedious, Kysely, pg, Redis—unchanged.
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 DBQUICK START
npm install edgewire
# or
bun add edgewireimport { 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.
| Feature | Hyperdrive | edgewire |
|---|---|---|
| Protocols | PostgreSQL only | Any TCP (entire net.Socket ecosystem) |
| Node.js libraries | PostgreSQL drivers | All TCP libraries |
| Setup | Dashboard config | Just code |
| Connection pooling | Managed | Durable Objects + Effect.ts |
| TLS | Built-in | Cloudflare 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, SyslogTLS / 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
tlsmodule. 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.