Bindings — How a Worker Reaches Everything Else
Hoplink needs a place to store its links. In a traditional app you would paste a database URL and a password into an environment file, create a client, and hope the secret never leaks. Workers use a different idea: you declare what the Worker is allowed to use, and the runtime hands it over, ready to go.
The rule
Declare it in config; receive it on
env; never hard-code credentials.
A binding is a named connection from your Worker to another resource: a KV namespace, an R2 bucket, a D1 database, a Durable Object class, a queue, another Worker, a secret or a plain variable. You declare bindings in the Wrangler configuration file, wrangler.jsonc (older projects use wrangler.toml):
{
"name": "hoplink",
"main": "src/index.js",
"compatibility_date": "2026-09-01",
"kv_namespaces": [
{ "binding": "LINKS", "id": "<namespace id>" }
],
"vars": { "SITE_NAME": "Hoplink" }
}At runtime the same name appears as a property of env:
export default {
async fetch(request, env, ctx) {
const target = await env.LINKS.get("cat");
return new Response(`${env.SITE_NAME}: ${target}`);
},
};Notice what is missing: no connection string, no API key, no client library. The binding is the permission. A Worker can only touch resources that appear in its config, which makes mistakes and leaks far harder.
Secrets work the same way but are stored encrypted rather than in the file. You add one with wrangler secret put ADMIN_TOKEN, and it arrives as env.ADMIN_TOKEN. Running wrangler types generates a TypeScript Env interface from your config, so your editor knows every binding.
A service binding connects one Worker to another. Hoplink's main Worker could call a separate hoplink-auth Worker through env.AUTH.fetch(request) — a direct call inside Cloudflare, not a trip over the public internet.
The shortcut
Whenever code needs something outside the Worker, ask "What is its binding name?" If the answer is a URL plus a password, you are probably reaching around the platform instead of through it.
Name it
The admin page needs a shared password. Call the binding
ADMIN_TOKEN.
Trap: reading env at module top level, outside any handler. Bindings are delivered per invocation, so reach for them inside fetch (or pass env down to helpers).
Drills
Answer fast — trust the rule.
The config declares a KV namespace with "binding": "LINKS". How does the code reach it?
Where should Hoplink's admin password live?
What is the main security benefit of bindings?
Hoplink's main Worker should call the separate hoplink-auth Worker. What fits best?
Remember
- Declare it in config; receive it on
env; never hard-code credentials. - Shortcut: "what is its binding name?" — a URL plus password is a warning sign.
- Trap: secrets in
varsor code, and touchingenvoutside a handler.
