This is a package that aims to provide a single solution for managing dynamic binds for Cloudflare Workers.
- (Optional) Worker creates a new D1 database via the Cloudflare API.
- Worker triggers a GitHub workflow to add the new binding.
- GitHub Actions modifies wrangler.toml and triggers a new deployment.
It's important to highlight that this action will parse and then stringify your wrangler configuration. It will look a bit different than the original because of how dynamic toml is.
For clarification, the new binding will not be available until the next deployment. It is in your control how you want to initiate the next deployment, some scenarios could be for example:
- Automatically when a commit is pushed to the main branch
- Automatically open a pull request in the workflow and approve manually
- Automatically when a deployment workflow has been approved manually
Right now only D1 databases are supported, feel free to adapt the action script to provide more options.
- D1 Databases
- KV Namespaces
- Durable Objects
- R2 buckets
- Queues
- A GitHub token with
workflow
permissions to your worker repository. - (Optional) A Cloudflare token with
d1:write
permissions to create new databases.
- Create a new workflow in the
.github/workflows
path, for example calleddynamic-bindings.yml
- Use the
@nora-soderlund/cloudflare-dynamic-bindings
action to modify your wrangler.toml file on demand
name: Dynamic bindings
on:
workflow_dispatch:
inputs:
bindings:
required: true
description: An JSON array of bindings to add.
permissions:
contents: write
jobs:
create-dynamic-bindings:
name: Create dynamic bindings
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Create dynamic bindings
uses: nora-soderlund/[email protected]
with:
file: ./wrangler.toml # change the path to your wrangler.toml file or just omit this line
bindings: ${{ inputs.bindings }}
- name: Commit new bindings
run: |
git config --global user.name 'GitHub Actions'
git config --global user.email ''
git commit -am "Add dynamic bindings"
git push
- Install the
@nora-soderlund/cloudflare-dynamic-bindings
package, e.g.
npm install @nora-soderlund/[email protected]
- Create a dynamic binding using the
createWranglerBinding
function, e.g.
import { createWranglerBinding } from "@nora-soderlund/cloudflare-dynamic-bindings";
import type { RepositoryProperties } from "@nora-soderlund/cloudflare-dynamic-bindings";
const repositorySettings: RepositoryProperties = {
owner: "nora-soderlund",
repository: "cloudflare-dynamic-bindings",
workflow: "dynamic-bindings.yml" // use the file name you chose for setting up the repository
};
export default {
async fetch(request: Request, env: Env, context: ExecutionContext): Promise<Response> {
if(!request.cf) {
throw new Error("Request is missing `cf` object.");
}
let database = env[`DATABASE_${request.cf.colo}`];
if(!database) {
// avoid blocking the response because we won't do anything with the outcome regardless
context.waitUntil(new Promise(async (resolve) => {
// create a new database for this colo using the Cloudflare API
// ...
// trigger a new binding configuration for the new database
await createWranglerBinding(repositorySettings, env.GITHUB_TOKEN, {
type: "D1",
binding: `DATABASE_${request.cf!.colo}`,
environments: [
{
databaseId: "e4ccd0df-f2ca-4d06-ab56-67d8d0373192", // use the new database
databaseName: "DATABASE_CPH"
}
]
});
resolve();
}));
// route to a fallback database, because the new binding is not yet available until the next deployment
database = env.DEFAULT_DATABASE;
}
// ...
}
}
Now, when you create a wrangler binding via your worker, a commit is pushed to your primary branch with the new binding. It is now expected that you have a workflow that is triggered on new commits and deploys your worker.