--- title: 'Cloudflare' description: 'Run bknd inside Cloudflare Worker' --- import InstallBknd from '/snippets/install-bknd.mdx'; ## Installation To get started with Cloudflare Workers and bknd you can either install the package manually, and follow the descriptions below, or use the CLI starter: Create a new Cloudflare CLI starter project by running the following command: ```sh npx bknd create -i cloudflare ``` Create a new cloudflare worker project by following the [official guide](https://developers.cloudflare.com/workers/get-started/guide/), and then install bknd as a dependency: ## Serve the API If you don't choose anything specific, the following code will use the `warm` mode and uses the first D1 binding it finds. See the chapter [Using a different mode](#using-a-different-mode) for available modes. ```ts src/index.ts import { serve, d1 } from "bknd/adapter/cloudflare"; // scans your environment for the first D1 binding it finds export default serve(); // manually specifying a D1 binding: export default serve({ app: ({ env }) => d1({ binding: env.D1_BINDING }) }); // or specify binding using `bindings` export default serve({ bindings: ({ env }) => ({ db: env.D1_BINDING }) }); // or use LibSQL export default serve({ app: ({ env }) => ({ url: env.DB_URL }) }); ``` For more information about the connection object when using LibSQL, refer to the [Database](/usage/database) guide. Now run the worker: ```bash wrangler dev ``` And confirm it works by opening [http://localhost:8787](http://localhost:8787) in your browser. ## Serve the Admin UI Now in order to also server the static admin files, you have to modify the `wrangler.toml` to include the static assets. You can do so by either serving the static using the new [Assets feature](https://developers.cloudflare.com/workers/static-assets/), or the deprecated [Workers Site](https://developers.cloudflare.com/workers/configuration/sites/configuration/). Make sure your assets point to the static assets included in the bknd package: ```toml wrangler.toml assets = { directory = "node_modules/bknd/dist/static" } ``` Make sure your site points to the static assets included in the bknd package: ```toml wrangler.toml [site] bucket = "node_modules/bknd/dist/static" ``` And then modify the worker entry as follows: ```ts {2, 6} src/index.ts import { serve } from "bknd/adapter/cloudflare"; import manifest from "__STATIC_CONTENT_MANIFEST"; export default serve({ app: () => ({/* ... */}), manifest }); ``` ## Adding custom routes You can also add custom routes by defining them after the app has been built, like so: ```ts {5-7} import { serve } from "bknd/adapter/cloudflare"; export default serve({ // ... onBuilt: async (app) => { app.server.get("/hello", (c) => c.json({ hello: "world" })); } }); ``` The property `app.server` is a [Hono](https://hono.dev/) instance, you can literally anything you can do with Hono. ## Using a different mode With the Cloudflare Workers adapter, you're being offered to 4 modes to choose from (default: `warm`): | Mode | Description | Use Case | |:----------|:-------------------------------------------------------------------------------------------|:----------------------------------------------------------------------------------------------------------------------------------------| | `fresh` | On every request, the configuration gets refetched, app built and then served. | Ideal if you don't want to deal with eviction, KV or Durable Objects. | | `warm` | It tries to keep the built app in memory for as long as possible, and rebuilds if evicted. | Better response times, should be the default choice. | | `cache` | The configuration is fetched from KV to reduce the initial roundtrip to the database. | Generally faster response times with irregular access patterns. | | `durable` | The bknd app is ran inside a Durable Object and can be configured to stay alive. | Slowest boot time, but fastest responses. Can be kept alive for as long as you want, giving similar response times as server instances. | ### Modes: `fresh` and `warm` To use either `fresh` or `warm`, all you have to do is adding the desired mode to `cloudflare. mode`, like so: ```ts import { serve } from "bknd/adapter/cloudflare"; export default serve({ // ... mode: "fresh" // mode: "fresh" | "warm" | "cache" | "durable" }); ``` ### Mode: `cache` For the cache mode to work, you also need to specify the KV to be used. For this, use the `bindings` property: ```ts import { serve } from "bknd/adapter/cloudflare"; export default serve({ // ... mode: "cache", bindings: ({ env }) => ({ kv: env.KV }) }); ``` ### Mode: `durable` (advanced) To use the `durable` mode, you have to specify the Durable Object to extract from your environment, and additionally export the `DurableBkndApp` class: ```ts import { serve, DurableBkndApp } from "bknd/adapter/cloudflare"; export { DurableBkndApp }; export default serve({ // ... mode: "durable", bindings: ({ env }) => ({ dobj: env.DOBJ }), keepAliveSeconds: 60 // optional }); ``` Next, you need to define the Durable Object in your `wrangler.toml` file (refer to the [Durable Objects](https://developers.cloudflare.com/durable-objects/) documentation): ```toml [[durable_objects.bindings]] name = "DOBJ" class_name = "DurableBkndApp" [[migrations]] tag = "v1" new_classes = ["DurableBkndApp"] ``` Since the communication between the Worker and Durable Object is serialized, the `onBuilt` property won't work. To use it (e.g. to specify special routes), you need to extend from the `DurableBkndApp`: ```ts import type { App } from "bknd"; import { serve, DurableBkndApp } from "bknd/adapter/cloudflare"; export default serve({ // ... mode: "durable", bindings: ({ env }) => ({ dobj: env.DOBJ }), keepAliveSeconds: 60 // optional }); export class CustomDurableBkndApp extends DurableBkndApp { async onBuilt(app: App) { app.modules.server.get("/custom/endpoint", (c) => c.text("Custom")); } } ``` In case you've already deployed your Worker, the deploy command may complain about a new class being used. To fix this issue, you need to add a "rename migration": ```toml [[durable_objects.bindings]] name = "DOBJ" class_name = "CustomDurableBkndApp" [[migrations]] tag = "v1" new_classes = ["DurableBkndApp"] [[migrations]] tag = "v2" renamed_classes = [{from = "DurableBkndApp", to = "CustomDurableBkndApp"}] deleted_classes = ["DurableBkndApp"] ``` ## D1 Sessions (experimental) D1 now supports to enable [global read replication](https://developers.cloudflare.com/d1/best-practices/read-replication/). This allows to reduce latency by reading from the closest region. In order for this to work, D1 has to be started from a bookmark. You can enable this behavior on bknd by setting the `d1.session` property: ```typescript src/index.ts import { serve } from "bknd/adapter/cloudflare"; export default serve({ // currently recommended to use "fresh" mode // otherwise consecutive requests will use the same bookmark mode: "fresh", // ... d1: { // enables D1 sessions session: true, // (optional) restrict the transport, options: "header" | "cookie" // if not specified, it supports both transport: "cookie", // (optional) choose session constraint if not bookmark present // options: "first-primary" | "first-unconstrained" first: "first-primary" } }); ``` If bknd is used in a stateful user context (like in a browser), it'll automatically send the session cookie to the server to set the correct bookmark. If you need to manually set the bookmark, you can do so by setting the `x-cf-d1-session` header: ```bash curl -H "x-cf-d1-session: " ... ```