This bridge makes it possible to interact with Cloudflare Workers bindings(like KV, D1, etc...) runtime APIs in local development.
In a nutshell, you can use actual KV, D1 APIs and values during
vite dev
! 😉
This bridge has 2 components.
- Module: Mock module to be injected into the user application
- written as pure ESM
- Worker: Proxy worker to be called by the bridge module
- hosted by
wrangler dev (--remote)
in advance
- hosted by
Since bridge module itself is platform agnostic, you can use it on any platform|environment.
- Vite based meta frameworks(Node.js)
- CLI tools(Bun, Node.js)
- Static Site Generation, Pre-rendering(Bun, Node.js)
- Cloudflare Workers in local(
warngler dev
) - etc...
0️⃣ Install it as usual.
npm install -D cfw-bindings-wrangler-bridge
1️⃣ Set up your wrangler.toml
properly and start wrangler dev
process.
wrangler dev ./node_modules/cfw-bindings-wrangler-bridge/worker.js --remote
Of course you can interact with local environment by omitting --remote
.
2️⃣ Create bridge and use it anywhere in your app.
import { createBridge } from "cfw-bindings-wrangler-bridge";
// Default origin is `http://127.0.0.1:8787`
const bridge = createBridge();
// Or
// const bridge = createBridge("http://localhost:3000");
/** @type {import("@cloduflare/workers-types").KVNamespace} */
const MY_KV = bridge.KVNamespace("MY_KV");
// For TypeScript
// const MY_KV = bridge.KV<KVNamespace>("MY_KV");
// ✌️ This is production KV!
await MY_KV.put("foo", "bar");
await MY_KV.get("foo"); // "bar"
Type definitions should be handled by yourself. 😅
- KV namespace
- All operations and arguments are supported 💯
bridge.KVNamespace()
- Service
- All operations and arguments are supported 💯
bridge.Fetcher()
- R2 bucket
- All operations and arguments are supported 💯
bridge.R2Bucket()
- D1 database
- All operations and arguments are supported 💯
bridge.D1Database()
- Queue(producer only)
- All operations and arguments are supported 💯
bridge.Queue()
- More to come...
If you are using REST API in your CLI, now you can replace it.
-const putKV = async (API_KEY, API_URL, [key, value]) => {
- const res = await fetch(`${API_URL}/values/${key}`, {
- method: "PUT",
- headers: { Authorization: `Bearer ${API_KEY}` },
- body: value,
- });
-
- const json = await res.json();
- if (!json.success)
- throw new Error(json.errors.map(({ message }) => message).join("\n"));
-};
+import { createBridge } from "cfw-bindings-wrangler-bridge";
+
+const putKV = async (KV_BINDING_NAME, [key, value]) => {
+ const KV = createBridge().KVNamespace(KV_BINDING_NAME);
+ await KV.put(key, value);
+};
// server.hooks.js
import { createBridge } from "cfw-bindings-wrangler-bridge";
import { dev } from "$app/environment";
export const handle = async ({ event, resolve }) => {
// Will be removed if `dev === false`
if (dev) {
const bridge = createBridge();
event.platform = {
env: {
SESSIONS: bridge.KVNamespace("SESSIONS"),
TODOS: bridge.D1Database("TODOS"),
},
};
}
return resolve(event);
};
---
// your-page.astro
import { getRuntime } from "@astrojs/cloudflare/runtime";
import { createBridge } from "cfw-bindings-wrangler-bridge";
let runtime = getRuntime(Astro.request) ?? {};
if (import.meta.env.DEV) {
const bridge = createBridge();
runtime.env = {
NEWS: bridge.KVNamespace("NEWS"),
};
}
---
<!-- ... -->
The instances and values available from this module are not 100% compatible.
For example,
- Binding instances
- The class constructors like
KVNamespace
,R2Object
(akaHeadResult
) are not publicly exposed
- The class constructors like
- Enumerable instance properties
- Read-only properties are emulated by simple implementation
- Some private properties and methods are included
- Exception
- Not a specific error like
TypeError
, but just anError
- Not a specific error like
- etc...
But I don't think there are any problems in practical use.
Current wrangler
implementation does not allow us to mix wrangler dev (--local)
services and wrangler dev --remote
services.
See also cloudflare/workers-sdk#1182
But with this bridge, you can get over it.
// Normal mode
// const MY_SERVICE = bridge.Fetcher("MY_SERVICE");
// Direct mode
const MY_SERVICE = bridge.Fetcher("", "http://127.0.0.1:8686");
With direct mode, you can mix wrangler dev --remote
and wrangler dev (--local)
.
At this time, however, the value of request.origin
will be different from the actual environment.
- Why not use REST API?
- REST API cannot offer
--local
behavior - Not all bindings are supported
- REST API cannot offer
- How about using
wrangler
CLI commands?- Features are limited too, no KV metadata support, etc...
wrangler.unstable_dev()
is better?- Maybe? but it is literally unstable
- I'm not sure how to ensure
await worker.stop()
on Vite process exit- Side-effect should be avoided...
- Performance may suffer if repeating start/stop on every call?
- I don't want to care which version of
wrangler
to be used, supported- Someone may use a fixed version of
wrangler
for some reason
- Someone may use a fixed version of