Starting a Cloudflare Worker from scratch
While tooling for web development is getting more and more complex, with CLIs for your CLIs becoming a thing, sometimes it's worth taking a step back, and realizing that doing things from scratch isn't so hard.
In this article, we're going to create and deploy a Cloudflare Worker without a CLI.
Table of contents
Steps to "Hello World"
- Make a new directory, and cd into it
mkdir my-worker/
cd my-worker/
- Initialize the npm project
npm init -y
- Install some packages
npm install -D typescript wrangler @cloudflare/workers-types
- Create a
tsconfig.jsonfile
touch tsconfig.json
After running the above command, paste the following into tsconfig.json:
//tsconfig.json
{
"compilerOptions": {
"noEmit": true,
"module": "esnext",
"target": "esnext",
"lib": ["esnext"],
"strict": true,
"moduleResolution": "node",
"types": ["@cloudflare/workers-types"]
}
}
- Make a
src/directory, create an index.ts file inside
mkdir src/
touch src/index.ts
After running the above command, paste the following into index.ts:
// index.ts
export default {
fetch: () => {
return new Response('Hello World!');
},
};
- Create a
wrangler.tomlfile
touch wrangler.toml
After running the above command, paste the following into wrangler.toml:
# wrangler.toml
name = "my-worker"
main = "src/index.ts"
compatibility_date = "2023-12-22"
- Login to wrangler
npx wrangler login
- Deploy your Worker
npx wrangler deploy
You can now visit the URL that wrangler outputs, and you should see "Hello World" in your browser.
- (optionally) Install vitest, add a test script
npm install vitest -D
touch src/index.test.ts
After running the above commands, paste the following into src/index.test.ts:
import { unstable_dev } from 'wrangler';
import type { UnstableDevWorker } from 'wrangler';
import { describe, expect, it, beforeAll, afterAll } from 'vitest';
describe('Worker', () => {
let worker: UnstableDevWorker;
beforeAll(async () => {
worker = await unstable_dev('src/index.ts', {
experimental: { disableExperimentalWarning: true },
});
});
afterAll(async () => {
await worker.stop();
});
it('should return Hello World', async () => {
const resp = await worker.fetch();
if (resp) {
const text = await resp.text();
expect(text).toMatchInlineSnapshot(`"Hello World!"`);
}
});
});
Finally, in package.json replace the test script with:
"test": "vitest"
Summary
There you have it, a Cloudflare Worker from scratch.
While it's not strictly better than just running
npx wrangler@2 init my-worker2 -y
(which is the exact CLI command you'd run to get the project above)
At least this way you understand what's happening before reaching for tooling to automate it.

