Cloudflare Workers have been steadily growing in popularity — and for a good reason. They’re currently one of the top Function as a Service (FaaS) offerings, utilizing V8 isolates for 0ms cold starts and Cloudflare’s edge infrastructure for low latency. Overall, Workers provide excellent performance and vast scalability at an affordable price.
With that said, arguably the only aspect of Workers left to consider is the development experience. Even though Cloudflare provides high-quality development resources such as the Wrangler CLI, the Workers Dashboard, and detailed documentation, it can still be hard to develop for Workers’ unique environment. As V8 isolates are somewhere in between Node.js and standard browsers, they require custom solutions and APIs for things like storage or WebSocket access.
Usually, you’d use Wrangler CLI and, more specifically, the wrangler dev
command to connect with Cloudflare’s servers and remotely develop for the Workers platform. This not only requires a good internet connection, but also limits your feature set to what your current Cloudflare plan allows. Not to mention polluting the production environment (e.g., with key-value (KV) storage) or having limited options for automated testing and CI/CD integration. That’s a sub-optimal development experience.
Thankfully, with a new tool called Miniflare, all of these issues go away.
Miniflare is an open source Cloudflare Workers simulator that’s implemented with TypeScript and Node.js and can run locally without needing an internet connection.
Now, Miniflare has only recently become an official part of the Cloudflare Workers ecosystem. Because of that, it hasn’t yet been popularized or integrated into the ecosystem as much as Wrangler CLI has. However, it’s already fully-featured and has detailed documentation.
Miniflare simulates the entire Workers environment with runtime, KV storage, and supported Web Standards included. It also implements newer Workers features, such as Durable Objects and even early-access ones like WebSocket connection. Let’s see how it can make Workers development faster and easier!
To get started with Miniflare, all you need is Node.js and npm:
mkdir worker cd worker npm init -y npm install -D esbuild miniflare @cloudflare/workers-types
Additionally, use your bundler of choice if you intend on using modules or something like TypeScript. For this example, we’ll use esbuild — an extremely fast JS/TS bundler — and the @cloudflare/workers-types package to get the best TypeScript development experience possible.
Turning to config files, first set up your package.json
file by adding the main
field and some scripts.
{ "main": "./dist/index.js", "scripts": { "build": "esbuild --bundle --sourcemap --outdir=dist ./src/index.ts", "dev": "miniflare --watch --debug" } }
Next, move over to wrangler.toml
.
name = "esbuild-worker" type = "javascript" account_id = "" workers_dev = true route = "" zone_id = "" kv_namespaces = [ { binding = "COUNTER_NAMESPACE", id = "", preview_id = "" }, ] [build] command = "npm run build" [build.upload] format = "service-worker"
If you’ve used the Wrangler CLI before, you’ll feel right at home. Miniflare accepts and respects the Wrangler CLI’s configuration keys, with an additional \[miniflare\]
section for Miniflare-specific options.
Lastly, to make TypeScript work properly, create a tsconfig.json
file referencing the Cloudflare Workers types.
{ "compilerOptions": { "target": "ES2020", "module": "CommonJS", "lib": ["ES2020"], "types": ["@cloudflare/workers-types"] } }
With Miniflare set up, you can now start working on your new Worker — just like that! Create a src/index.ts
file as your entry point, and use the addEventListener()
function to listen for incoming requests.
addEventListener("fetch", (event) => { event.respondWith(new Response("test")); });
The workflow and code are the same as when using Wrangler CLI.
To test your Worker, use the npm run dev
command to start Miniflare’s development environment. Open the local URL provided by the CLI (port 8787
by default), and you should see the Worker’s output.
Miniflare can automatically rebuild your Worker after every file change and also display helpful debugging information. For that to work, you should use the \--watch
and \--debug
flags respectively.
Like I’ve said, Miniflare simulates the entire Workers environment, including all of its dedicated features, like KV. Let’s see how it works in practice.
First, create a separate src/bindings.d.ts
file. You can use it to declare globals specific to your Workers like KV namespaces or env variables. In this case, that’s a single KV namespace.
export {}; declare global { const COUNTER_NAMESPACE: KVNamespace; }
Next, in your main file, create a function that will handle the request and interact with the KV storage.
const handleRequest = async (request: Request) => { const url = new URL(request.url); const currentValue = await COUNTER_NAMESPACE.get(url.pathname); const newValue = (parseInt(currentValue || "0") + 1).toString(); await COUNTER_NAMESPACE.put(url.pathname, newValue); return new Response(newValue); }; addEventListener("fetch", (event) => { event.respondWith(handleRequest(event.request)); });
Now, you should see the counter increasing on each page refresh, meaning the KV storage works.
So, with local development, detailed debugging information, fast reloads, and access to all of Workers features, Miniflare is already a big win. With that said, probably its biggest advantage — yes, one we haven’t talked about yet — is its API.
With the Miniflare API, you can automate the entire process of building, running, and testing your Workers. You can also control things like KV storage or Durable Objects outside the Worker, leading to a whole new set of possibilities for Worker testing.
To play with the Miniflare API, create a new start.js
file in your root directory. Inside the file, require()
Miniflare and use its API to make requests to the Worker and access KV storage.
const { Miniflare } = require("miniflare"); const mf = new Miniflare(); const makeRequest = async () => { const res = await mf.dispatchFetch("http://localhost:8787/"); return res.text(); }; const testNamespace = async () => { const counterNamespace = await mf.getKVNamespace("COUNTER_NAMESPACE"); const count = await counterNamespace.get("/"); console.log("KV:", count); return count; }; const testRequests = async (times) => { for (let i = 0; i < times; i++) { console.log("Response:", await makeRequest()); } }; const test = async () => { await testRequests(3); await testNamespace(); }; test();
A dedicated Miniflare
instance handles building and loading the Worker, while simultaneously providing access to the necessary APIs.
The above functions serve as an example of how you can test your Worker in a real-world scenario. testNamespace()
retrieves a specific value from the KV storage’s COUNTER\_NAMESPACE
, while the testRequests()
function sends three requests to the Worker, making it write to KV, and logs the results.
Running the above snippet should output the following results:
Response 1 Response 2 Response 3 KV: 3
As you can see, Miniflare is a very capable simulator and development tool. Thanks to its vast features and advantages, I can recommend it for all Worker development and testing purposes. It dramatically accelerates and simplifies the whole process while allowing for much more testing possibilities.
With that said, Wrangler CLI isn’t going anywhere, and it’s still the best tool for deploying and verifying your Workers in the production environment. Testing your Worker on actual Cloudflare servers with actual production-level environment is something Miniflare can’t do.
All in all, it looks like Cloudflare Workers have a bright future, and I can’t wait to see what comes next!
Install LogRocket via npm or script tag. LogRocket.init()
must be called client-side, not
server-side
$ npm i --save logrocket // Code: import LogRocket from 'logrocket'; LogRocket.init('app/id');
// Add to your HTML: <script src="https://cdn.lr-ingest.com/LogRocket.min.js"></script> <script>window.LogRocket && window.LogRocket.init('app/id');</script>
Hey there, want to help make our blog better?
Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.
Sign up nowLearn how to manage memory leaks in Rust, avoid unsafe behavior, and use tools like weak references to ensure efficient programs.
Bypass anti-bot measures in Node.js with curl-impersonate. Learn how it mimics browsers to overcome bot detection for web scraping.
Handle frontend data discrepancies with eventual consistency using WebSockets, Docker Compose, and practical code examples.
Efficient initializing is crucial to smooth-running websites. One way to optimize that process is through lazy initialization in Rust 1.80.