Functions, bindings, and operations
Bind resources and debug production
Inject D1, KV, R2, services, variables, and secrets through environment bindings, then verify production without exposing credentials.
8 minute lesson
Pages Functions access configured resources through context.env. A KV namespace, a D1 database, an R2 bucket, a service binding, plain variables, and secrets all arrive as properties on that object:
export async function onRequestGet(context) {
const settings = await context.env.CONFIG.get('settings', 'json')
return Response.json({ theme: settings?.theme ?? 'default' })
}
The binding name (CONFIG here) is configuration, defined per project and per environment. Preview and production can point the same name at different namespaces, which is exactly what you want — and exactly why you must test both targets separately. If you use TypeScript, regenerate types with npx wrangler types whenever bindings change.
The missing-binding failure
This is the error you will meet first:
TypeError: Cannot read properties of undefined (reading 'get')
context.env.CONFIG was undefined because the deployed environment has no binding with that name. Locally everything worked, because your local configuration had it. A successful local simulation does not prove the production binding exists or points at the intended resource. Check the project’s bindings for the specific environment before reading any stack trace further.
Secrets are not vars
Plain-text vars are for public values, like a Turnstile site key. Anything private goes into encrypted secrets, set per environment:
npx wrangler pages secret put RESEND_API_KEY --project-name my-site
# ✨ Success! Uploaded secret RESEND_API_KEY
Store secrets in encrypted project settings, not vars or source. For local development, put them in a gitignored .dev.vars file. Both appear identically as context.env.RESEND_API_KEY in code; only their storage differs.
Debug production without leaking it
Stream live Function logs from a deployment:
npx wrangler pages deployment tail --project-name my-site
Every request and console.log appears as it happens. Make those logs useful and safe: attach a request ID so you can follow one request, log the resource name you resolved, and return safe errors to clients — a generic 500 message outside, the detail in the log. Never log the secret values themselves.
Now bind a practice KV namespace to a Pages project, read one non-secret value from a Function, deploy, and verify from logs and safe metadata that production resolved the resource name and environment you intended.
Lesson completed