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.
Pages Functions reach 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. That’s exactly what you want, and exactly why you must test both targets separately.
If you use TypeScript, run npx wrangler types whenever bindings change, so context.env stays typed.
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 run does not prove the production binding exists or points at the intended resource. Check the project’s bindings for that 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
For local development, put secrets in a gitignored .dev.vars file. Both kinds appear the same way in code, as context.env.RESEND_API_KEY. Only their storage differs. Never put a secret in vars or in source.
Debug production without leaking it
Stream live Function logs from a deployment:
npx wrangler pages deployment tail --project-name my-site
Every request and every 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, not its contents. Return safe errors to clients: a generic 500 message outside, the detail in the log. And never log the secret values themselves.
Try this: bind a practice KV namespace to a Pages project, read one non-secret value from a Function, deploy, and check the logs to confirm production resolved the resource name and environment you intended.
Lesson completed