Introducing MySQL connections in Cloudflare Workers over sockets

5 months ago

Introducing MySQL connections in Cloudflare Workers over sockets

Previously MySQL connections in Cloudflare Workers were only done possible through Cloudflare Tunnels.

Here's to introducing a TCP socket implementation!

In November, 2021, Cloudflare released relational database connectors that works through a Cloudflare Tunnel to a private network, because there were no TCP socket runtimes in the workers environment yet.

In May, 2023, Cloudflare released the Socket API which introduces TCP sockets! This is great, because now we can connect directly to a MySQL server, over a direct two-way communication.

I took it upon myself to adapt the MySQL package for Node to be runnable on the edge! This required 3 actions:

The package is available at cloudflare-mysql!

Creating a Stream API polyfill

A Stream API interface was needed specifically for the EventEmitter class and without wanting to explode the forked package in dozen of individual changes, I made a polyfill specifically for the Stream API which only targets the methods used by the MySQL package.

Creating a Net API polyfill

The Net API provides the Socket API, which is what the MySQL package uses for creating the TCP socket. I adapted this API to simply redirect to the Cloudflare Socket API, and implement the timeout methods.

Modifying the MySQL package

Without digging too much, I had to remove the previous Node.js dependencies, add the polyfill packages, and update some functions. An important update was changing the string parser to use the TextDecoder instead, and changing the buffer functions - especially for the password encryption and handshake.

The result

It really was not much more than that to do. I got it fully working to the extent that I tested it, primarily connecting, querying, and escaping.

If this is something that you've been waiting for, it's as easy as this to get started:

import { createConnection } from "cloudflare-mysql"; export default { async fetch(request: Request, env: Env, context: ExecutionContext) { const url = new URL(request.url); const searchInput = url.searchParams.get("search"); const result = await new Promise((resolve) => { const connection = createConnection({ host: "localhost", user: "root", password: "password", database: "database" }); connection.connect((error) => { if(error) throw new Error(error.message); connection.query("SELECT title FROM articles WHERE title LIKE CONCAT('%', ?, '%')", [ searchInput ], (error, rows, fields) => { connection.end(); resolve({ fields, rows }); }); }); }); return new Response(JSON.stringify(result, undefined, 4)); } }

You can find the full source code and worker example at the cloudflare-mysql repository!