Why Cloudflare Workers Don't Work With MongoDB
Cloudflare Workers is a great platform for deploying serverless JavaScript code, but has historically never supported raw sockets. In May of 2023 they announced support for a connect()
API which allows TCP Sockets to be created within Workers, but is not a direct replacement for Node.js’ net.Socket
API.
Many NPM packages - including MongoDB’s Node.js driver - rely on net.Socket
or tls.TLSSocket
to connect to and communicate with remote systems. Using these libraries directly from Cloudflare Workers has not been possible as a result of these missing APIs.
Cloudflare recently announced that more NPM packages would be supported on Cloudflare Workers, but for libraries that need net.Socket
or tls.TLSocket
access, will they work in a Cloudflare Workers environment?
Packages that could not be imported with
nodejs_compat
, even as a dependency of another package, will now load. This includes popular packages such as […]mongodb
, […] and many more.
Based on the blog post the Node.js driver should load, but can it be used?
Note that the fact that Workers can load, but not use the
mongodb
package was reported to Cloudflare at https://github.com/cloudflare/workers-sdk/issues/6684The Cloudflare blog post has since been updated to remove
mongod
from the list of useable packages to avoid further confusion.
Sample Application
To test the latest iteration of Cloudflare Workers compatibility flags we’ll be working with the following configuration:
src/worker.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
import { BSON, MongoClient } from 'mongodb';
export interface Env {
MONGODB_URI: string;
}
let client = null;
let requestCount = 0;
export default {
async fetch(request: Request, env: Env, _ctx: ExecutionContext): Promise<Response> {
console.log(JSON.stringify({ requestCount }));
requestCount += 1;
client ??= new MongoClient(env.MONGODB_URI, {
maxPoolSize: 1, minPoolSize: 0,
serverSelectionTimeoutMS: 5000,
});
const db = client.db('test');
const coll = db.collection('test');
if ((await coll.countDocuments()) > 10) {
await coll.drop().catch(() => null);
}
await coll.insertOne({ a: 1 });
return new Response(BSON.EJSON.stringify(await coll.findOne({ a: 1 }), null, ' ', { relaxed: false }));
},
};
package.json
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
{
"name": "mongodb-cloudflare-example",
"version": "0.0.0",
"type": "module",
"private": true,
"scripts": {
"start": "wrangler dev"
},
"devDependencies": {
"@cloudflare/workers-types": "^4.20240603.0",
"ts-node": "^10.9.1",
"typescript": "^5.0.4",
"wrangler": "^3.59.0"
},
"dependencies": {
"mongodb": "^6.8.1"
}
}
wrangler.toml
1
2
3
4
5
6
name = "mongodb-cloudflare-example"
main = "src/worker.ts"
compatibility_flags = ["nodejs_compat_v2"]
[vars]
MONGODB_URI = "mongodb+srv://..."
To test the code above you would need to do the following:
1
2
npm install
npm run start
Evaluation
The default connection string format when connecting to your Atlas cluster is mongodb+srv
, which is what we included initially in the wrangler.toml
file.
The first time we run our test code however we’re unable to resolve the SRV connection format as it appears that dns.resolveTxt
is not implemented:
1
2
3
⎔ Starting local server...
{"requestCount":0}
[wrangler:err] Error: [unenv] dns.resolveTxt is not implemented yet!
Since Atlas allows you to also connect using the standard connection string format, let’s update the MONGODB_URI
in the wrangler.toml
to instead be mongodb://...
:
1
2
3
⎔ Starting local server...
{"requestCount":0}
[wrangler:err] MongoServerSelectionError: socket.once is not a function
Based on the above it appears events.once
is missing, which Node.js’ net
module exposes from an EventEmitter
import. I don’t think we’d be able to polyfill all this if that were considered the path forward 😓.
What about trying to configure wrangler
to connect to a local MongoDB instance (ex: mongodb://localhost:27017
)? Well in that case it will still fail, but at least it will fail differently:
1
2
3
⎔ Starting local server...
{"requestCount":0}
[wrangler:err] MongoServerSelectionError: [unenv] net.createConnection is not implemented yet!
Alternatives?
If you happen upon the article called “Create a REST API with Cloudflare Workers and MongoDB Atlas” you may be thinking there’s an alternate solution to be explored. MongoDB offered a REST-based interface to your data in Atlas via the Atlas Data API, however this product was recently deprecated and will be sunset.
A custom REST-based API would be a solution to working with your MongoDB data from within Cloudflare Workers, so until the runtime is updated to better support Node.js’ socket APIs, see the Data API deprecation page for some ideas.
Neurelo seems like a good option for getting a REST-based API off the ground with little effort.
Summary
Though module aliasing and polyfills might be an option for some functionality, it really seems like Cloudflare Workers just aren’t meant to work with Node.js’ socket APIs. As a result, libraries such as MongoDB’s Node.js driver simply won’t be able to connect to anything.
Some work was proposed by MongoDB’s team to allow a custom transport layer to be provided, however this would still require Cloudflare Workers to support Node.js’ TLS API as TLS cannot, for good reasons, be disabled for Atlas deployments.
Comments powered by Disqus.