Cloudflare

Deploy Nitro apps to Cloudflare.

#Cloudflare Workers

Preset: cloudflare_module

Read more in Cloudflare Workers.

Note

Integration with this provider is possible with zero configuration supporting workers builds (beta).

The following shows an example nitro.config.ts file for deploying a Nitro app to Cloudflare Workers.

nitro.config.ts
import { defineConfig } from "nitro";

export default defineConfig({
    preset: "cloudflare_module"
})

#Local Preview

You can use Wrangler to preview your app locally:

npm run build
npx wrangler dev

#Manual Deploy

After building your application you can manually deploy it with Wrangler.

First make sure to be logged into your Cloudflare account:

npx wrangler login

Then you can deploy the application with:

npx wrangler deploy

#Runtime Hooks

You can use the runtime hooks below to extend Worker handlers.

Read more in Docs > Plugins#nitro Runtime Hooks.

Note

The cloudflare:queue hook receives the message batch as batch and the cloudflare:email hook receives the incoming message as message. The older event field is deprecated for both hooks.

#Durable Objects

Export your Durable Object classes from your server.ts file. They work in your deployed Worker and during local development, including with Vite. You can also define them in other files and re-export them.

server.ts
import { DurableObject } from "cloudflare:workers";

export class Counter extends DurableObject {
  async fetch() {
    const count = (await this.ctx.storage.get<number>("count")) || 0;
    await this.ctx.storage.put("count", count + 1);
    return Response.json({ count: count + 1 });
  }
}

export default {
  fetch() {},
};

Note

server.ts needs a default export. If you only use it for your Durable Objects, keep the empty fetch handler above so requests continue to your routes.

Declare the binding and Durable Object export in your Wrangler configuration:

wrangler.json
{
  "durable_objects": {
    "bindings": [{ "name": "COUNTER", "class_name": "Counter" }]
  },
  "exports": {
    "Counter": { "type": "durable-object", "storage": "sqlite" }
  }
}

Access the binding from a Nitro handler:

routes/counter.ts
import { defineHandler } from "nitro";

export default defineHandler((event) => {
  const { COUNTER } = event.req.runtime!.cloudflare!.env;
  return COUNTER.get(COUNTER.idFromName("default")).fetch(event.req);
});

With Wrangler environments, declare the binding and export in the selected environment. Local Durable Object state persists in .wrangler/state/v3, including across development server reloads.

In development, Durable Objects share module state with your routes and pick up code changes without restarting the development server. A new class, or a class not declared in the Wrangler configuration, logs a warning.

To type the COUNTER binding with the Counter class, see Typing bindings.

#Additional Exports

Other classes, such as Workflows or WorkerEntrypoints, are exported from server.ts the same way:

server.ts
import {
  WorkerEntrypoint,
  WorkflowEntrypoint,
  type WorkflowEvent,
  type WorkflowStep,
} from "cloudflare:workers";

export class MyWorkflow extends WorkflowEntrypoint {
  async run(event: WorkflowEvent, step: WorkflowStep) {
    // ...
  }
}

export class MyEntrypoint extends WorkerEntrypoint {
  greet(name: string) {
    return `Hello ${name}`;
  }
}

Declare them in your Wrangler configuration, with a workflows binding or an exports entry:

wrangler.json
{
  "workflows": [{ "name": "my-workflow", "binding": "MY_WORKFLOW", "class_name": "MyWorkflow" }],
  "exports": {
    "MyEntrypoint": { "type": "worker" }
  }
}

#Scheduled Tasks (Cron Triggers)

When using Nitro tasks with scheduledTasks, Nitro automatically generates Cron Triggers in the wrangler config at build time.

nitro.config.ts
import { defineConfig } from "nitro";

export default defineConfig({
  preset: "cloudflare_module",
  experimental: {
    tasks: true,
  },
  scheduledTasks: {
    "* * * * *": ["cms:update"],
    "0 15 1 * *": ["db:cleanup"],
  }
})

No manual Wrangler configuration is needed. Nitro handles it for you.

#Cloudflare Workers with Durable Objects

Preset: cloudflare_durable

Read more in Durable Objects.

This preset extends cloudflare_module and routes requests through a Durable Object instance, enabling stateful features such as WebSocket support (via CrossWS) and in-memory state that persists across requests.

nitro.config.ts
import { defineConfig } from "nitro";

export default defineConfig({
  preset: "cloudflare_durable"
})

The preset entry exports a $DurableObject class. Declare its binding and export in your Wrangler configuration:

wrangler.json
{
  "durable_objects": {
    "bindings": [
      {
        "name": "$DurableObject",
        "class_name": "$DurableObject"
      }
    ]
  },
  "exports": {
    "$DurableObject": { "type": "durable-object", "storage": "sqlite" }
  }
}

You can use the cloudflare:durable:init runtime hook to run code when the Durable Object is initialized, and the cloudflare:durable:alarm hook to handle alarms.

#Tracing

🧪 Experimental!

When the experimental tracingChannel option is enabled, the Cloudflare presets report Nitro's tracing-channel events (h3 routes and middleware, srvx, unstorage operations, …) as custom spans, alongside Cloudflare's automatic instrumentation (fetch calls, KV reads, D1 queries, …), with no OpenTelemetry SDK required.

nitro.config.ts
import { defineConfig } from "nitro";

export default defineConfig({
  preset: "cloudflare_module",
  tracingChannel: true,
});

Tracing must be enabled on the Worker for spans to be recorded:

wrangler.jsonc
{
  "observability": {
    "traces": {
      "enabled": true
    }
  }
}

#Cloudflare Pages

Preset: cloudflare_pages

Read more in Cloudflare Pages.

Note

Integration with this provider is possible with zero configuration.

Warning

Cloudflare Workers is the new recommended preset for deployments. Consider Cloudflare Pages only if you need Pages-specific features.

The following shows an example nitro.config.ts file for deploying a Nitro app to Cloudflare Pages.

nitro.config.ts
import { defineConfig } from "nitro";

export default defineConfig({
    preset: "cloudflare_pages"
})

Nitro automatically generates a _routes.json file that controls which routes get served from files and which are served from the Worker script. The auto-generated routes file can be overridden with the config option cloudflare.pages.routes (read more).

#Local Preview

You can use Wrangler to preview your app locally:

npm run build
npx wrangler pages dev

#Manual Deploy

After building your application you can manually deploy it with Wrangler.

First make sure to be logged into your Cloudflare account:

npx wrangler login

Then you can deploy the application with:

npx wrangler pages deploy

#Deploy within CI/CD using GitHub Actions

Regardless of whether you're using Cloudflare Pages or Cloudflare Workers, you can use the Wrangler GitHub actions to deploy your application.

Note

Remember to instruct Nitro to use the correct preset. This is necessary for all presets, including cloudflare_pages.

#Environment Variables

Nitro allows you to universally access environment variables using process.env, import.meta.env, or the runtime config.

Note

Make sure to only access environment variables within the event lifecycle and not in global contexts, since Cloudflare only makes them available during the request lifecycle and not before.

Example: If you have set the SECRET and NITRO_HELLO_THERE environment variables, you can access them in the following ways:

import { defineHandler } from "nitro";
import { useRuntimeConfig } from "nitro/runtime-config";

console.log(process.env.SECRET) // note that this is in the global scope! so it doesn't actually work and the variable is undefined!

export default defineHandler((event) => {
  // note that all the below are valid ways of accessing the above mentioned variables
  useRuntimeConfig().helloThere
  useRuntimeConfig().secret
  process.env.NITRO_HELLO_THERE
  import.meta.env.SECRET
});

#Specify Variables in Development Mode

For development, you can use a .env or .env.local file to specify environment variables:

NITRO_HELLO_THERE="captain"
SECRET="top-secret"

Note

Make sure you add .env and .env.local to your .gitignore file so that you don't commit them, as they can contain sensitive information.

#Specify Variables for local previews

After building, when you try out your project locally with wrangler dev or wrangler pages dev, specify environment variables in a .dev.vars file in the root of your project (as described in the Pages and Workers documentation).

If you are using a .env or .env.local file while developing, your .dev.vars should be identical to it.

Note

Make sure you add .dev.vars to your .gitignore file so that you don't commit it, as it can contain sensitive information.

#Specify Variables for Production

For production, use the Cloudflare dashboard or the wrangler secret command to set environment variables and secrets.

#Specify Variables using wrangler.toml/wrangler.json

You can specify a custom wrangler.toml/wrangler.json file and define vars inside.

Warning

This isn't recommended for sensitive data like secrets.

Example:

# Shared
[vars]
NITRO_HELLO_THERE="general"
SECRET="secret"

# Override values for `--env production` usage
[env.production.vars]
NITRO_HELLO_THERE="captain"
SECRET="top-secret"

#Direct access to Cloudflare bindings

Bindings let you interact with resources from the Cloudflare platform, such as key-value data storage (KV) and serverless SQL databases (D1).

Read more in .

Tip

Nitro provides high-level APIs for primitives such as KV Storage and Database. Prefer them over depending directly on low-level platform APIs, for usage stability.

Read more in Database Layer.
Read more in KV Storage.

You can access bindings from the request event via event.req.runtime.cloudflare.env. For example, this is how you can access a D1 binding:

import { defineHandler } from "nitro";

defineHandler(async (event) => {
  const { env } = event.req.runtime.cloudflare
  const stmt = await env.MY_D1.prepare('SELECT id FROM table')
  const { results } = await stmt.all()
})

#Typing bindings

Use wrangler types to generate types for your bindings and the Workers runtime from your Wrangler configuration. It writes a worker-configuration.d.ts file declaring a global Env interface.

package.json
{
  "scripts": {
    "postinstall": "wrangler types",
    "typecheck": "wrangler types && tsc --noEmit"
  }
}

Include the generated file in your tsconfig.json. It also provides the runtime types, so you don't need @cloudflare/workers-types. Nitro enables the nodejs_compat flag for you, so also add @types/node (with wrangler) to your dev dependencies and include node for the Node.js APIs:

tsconfig.json
{
  "compilerOptions": {
    "types": ["./worker-configuration.d.ts", "node"]
  }
}

Bindings imported from cloudflare:workers are typed automatically:

import { defineHandler } from "nitro";
import { env } from "cloudflare:workers";

export default defineHandler(async () => {
  return env.MY_KV.get("key");
});

To type event.req.runtime.cloudflare.env too, augment the CloudflareEnv interface of srvx:

env.d.ts
declare module "srvx" {
  interface CloudflareEnv extends Env {}
}

export {};

Note

srvx must be resolvable from your project for the augmentation to apply. With pnpm, add it to your dev dependencies.

To type bindings to your own Durable Objects, Workflows and WorkerEntrypoints with their classes, point main in your Wrangler configuration to the file that exports them. Nitro still sets main to the built entry in the generated config.

wrangler.json
{
  "main": "./server.ts"
}

wrangler types then reads the classes from it:

worker-configuration.d.ts
interface Env {
  COUNTER: DurableObjectNamespace<import("./server").Counter>;
  MY_WORKFLOW: Workflow<Parameters<import("./server").MyWorkflow["run"]>[0]["payload"]>;
  API: Service<typeof import("./server").Api>;
}

Tip

Run wrangler types again after changing your Wrangler configuration, or use wrangler types --check in CI to catch outdated types.

#Access to the bindings in local dev

In development mode, Nitro emulates the Cloudflare environment using Miniflare (the same workerd runtime used by Wrangler and Cloudflare Workers in production). This means bindings are available natively from the request event, with no separate proxy required.

The miniflare package is owned by your project: Nitro resolves it from your node_modules and offers to install it on first use.

To access bindings in dev mode, first define them. You can do this in a wrangler.jsonc/wrangler.json/wrangler.toml file (reading wrangler.jsonc, wrangler.toml and .dev.vars requires the wrangler package to be installed):

[vars]
MY_VARIABLE="my-value"

[[kv_namespaces]]
binding = "MY_KV"
id = "xxx"

Alternatively, you can define bindings inline in your nitro.config.ts using the cloudflare.wrangler option (it accepts the same shape as wrangler.json):

nitro.config.ts
import { defineConfig } from "nitro";

export default defineConfig({
  preset: "cloudflare_module",
  cloudflare: {
    wrangler: {
      vars: {
        MY_VARIABLE: "my-value",
      },
      kv_namespaces: [{ binding: "MY_KV", id: "xxx" }],
    },
  },
})

From now on, when running

npm run dev

you can access MY_VARIABLE and MY_KV from the request event as illustrated above.

A few things differ from wrangler dev:

  • Local data of the bindings (KV, D1, R2, ...) is persisted in .wrangler/state/v3, shared with wrangler dev.
  • The newest compatibility date supported by the installed miniflare is used, regardless of compatibility_date.
  • Static assets are served by Nitro. Local Durable Objects, Workflows and WorkerEntrypoints exported from server.ts are available; bindings to other workers (services, tail_consumers, external Durable Objects and Workflows) and queue consumers are not available.
  • When cloudflare.wrangler is set, it is merged with the nearest Wrangler config file.

#Wrangler environments

If you have multiple Wrangler environments, you can specify which one to use during local dev emulation with the cloudflare.wranglerEnv option:

nitro.config.ts
import { defineConfig } from "nitro";

export default defineConfig({
  preset: 'cloudflare_module',
  cloudflare: {
    wranglerEnv: 'preview'
  }
})

Nitro  builds full-stack servers that deploy anywhere.