Next-Cloudflare-Turbo Logo Mark@nct

Creating a Worker

Creating a new Cloudflare Worker in the monorepo

Open in Github

Overview

Estimated time: 5-10 minutes

This guide shows you how to create a new Cloudflare Worker within your Next-Cloudflare-Turbo monorepo structure. Workers are serverless functions that run on Cloudflare's edge network, perfect for API endpoints, middleware, or standalone applications.

This guide uses a PostHog Reverse Proxy as an example, but the same process applies to any Worker type.

For a comprehensive breakdown of Cloudflare Workers, see the Cloudflare Documentation.

Prerequisites


Creating a Worker

Navigate to the apps directory

From your project root, navigate to the apps directory where all applications are housed:

cd apps

Run the create-cloudflare command

Use Cloudflare's C3 CLI to create a new Worker project:

npm create cloudflare@latest -- worker-name

Replace worker-name with your desired Worker name (e.g., api-worker, auth-middleware, image-processor). In this example, it was called posthog.

The -- separates npm arguments from the package arguments, ensuring the worker name is passed correctly to the create-cloudflare CLI.

Configure the Worker options

You'll be prompted with several configuration questions. For a basic Worker setup, select:

  • What would you like to start with?: Hello World example
  • Which template would you like to use?: Worker only
  • Which language do you want to use?: TypeScript
  • Do you want to use git for version control?: No (you're already in a monorepo with git)
  • Do you want to deploy your application?: No (we'll make changes first)
 Create an application with Cloudflare Step 1 of 3

 In which directory do you want to create your application?
 dir ./posthog

 What would you like to start with?
 category Hello World example

 Which template would you like to use?
 type Worker only

 Which language do you want to use?
 lang TypeScript

 Copying template files
 files copied to project directory

 Updating name in `package.json`  
 updated `package.json`

 Installing dependencies
 installed via `npm install`

 Application created

 Configuring your application for Cloudflare Step 2 of 3

 Installing wrangler A command line tool for building Cloudflare Workers
 installed via `npm install wrangler --save-dev`

 Retrieving current workerd compatibility date
 compatibility date 2025-08-16

 Generating types for your application
 generated to `./worker-configuration.d.ts` via `npm run cf-typegen`

 You're in an existing git repository. Do you want to use git for version control?
│ no git

╰ Application configured 

╭ Deploy with Cloudflare Step 3 of 3

├ Do you want to deploy your application?
│ no deploy via `npm run deploy`

╰ Done

Verify the Worker structure

After creation, your Worker should have the following basic structure:

apps/
└── worker-name/
    ├── src/
    │   └── index.ts
    ├── wrangler.jsonc
    ├── package.json
    ├── tsconfig.json
    └── README.md

The default Worker scaffold includes unnecessary files, such as a .vscode directory (we use that in the monorepo root), .prettier files, etc. Delete what you don't need.

Test the Worker locally

Start the development server to ensure everything is working:

turbo run dev

Your Worker will be available at http://localhost:8787 and should display "Hello World!" when visited.

Press Ctrl+C to stop the development server when you're finished testing.

Replace the default "Hello World" code with your specific implementation.

In this example, we'll create a PostHog reverse proxy:

src/index.ts
const API_HOST = "eu.i.posthog.com"
const ASSET_HOST = "eu-assets.i.posthog.com"

async function handleRequest(request: Request, ctx: ExecutionContext) {
const url = new URL(request.url)
const pathname = url.pathname
const search = url.search
const pathWithParams = pathname + search

if (pathname.startsWith("/static/")) {
    return retrieveStatic(request, pathWithParams, ctx)
}
return forwardRequest(request, pathWithParams)
}

async function retrieveStatic(
request: Request,
pathname: string,
ctx: ExecutionContext
) {
let response = await caches.default.match(request)
if (!response) {
    response = await fetch(`https://${ASSET_HOST}${pathname}`)
    ctx.waitUntil(caches.default.put(request, response.clone()))
}
return response
}

async function forwardRequest(request: Request, pathWithSearch: string) {
const originRequest = new Request(request)
originRequest.headers.delete("cookie")
return await fetch(`https://${API_HOST}${pathWithSearch}`, originRequest)
}

export default {
// biome-ignore lint/correctness/noUnusedFunctionParameters: env param required to satisfy ExportedHandler<Env>
async fetch(request: Request, env: Env, ctx: ExecutionContext) {
    return handleRequest(request, ctx)
},
} satisfies ExportedHandler<Env>

Deploy the Worker

Once you're satisfied with your implementation, deploy to production:

npx wrangler deploy
You can also deploy later after further testing and configuration.

Understanding the Generated Files

src/index.ts

The main Worker code containing your request handler:

export default {
  async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
    return new Response('Hello World!');
  },
};

wrangler.jsonc

Configuration file for the Worker deployment:

{
  "$schema": "../../node_modules/wrangler/config-schema.json",
  "name": "posthog",
  "main": "src/index.ts",
  "compatibility_date": "2025-08-16",
  "observability": {
    "enabled": true
  },
  /**
   * Smart Placement
   * Docs: https://developers.cloudflare.com/workers/configuration/smart-placement/#smart-placement
   */
  "placement": { "mode": "smart" },
  "routes": [
    {
      "pattern": "ph.cording.dev",
      "custom_domain": true
    }
  ]
}
  • The default jsonc file will set the $schema directory as node_modules/wrangler/config-schema.json. As we are using a monorepo, the wrangler installation is hoisted to the root node_modules. In this instance, we change the path to ../../node_modules/wrangler/config-schema.json.
  • Smart Placement is activated in this configuration.
  • A custom domain is applied via the routes key. You can remove this, or change it to your own custom domain. If you remove it, the default workers domain will be used, which would be worker-name.your-cloudflare-username.workers.dev

Package Scripts

Common development commands available:

  • turbo run dev - Start local development server
  • npm run deploy - Deploy to production
  • npm run tail - View live logs
  • npm run cf-typegen - Generate TypeScript types

Next Steps

Now that you have a basic Worker created, you can:

  1. Customise the Worker logic in src/index.ts for your specific use case
  2. Add environment variables to wrangler.jsonc for configuration
  3. Configure bindings for databases, KV stores, or R2 buckets
  4. Set up routing in your main application to proxy requests to the Worker

How is this guide?

Last updated on