Environment Variables
Zero-config environment variable system powered by Hono — works across Node.js, Bun, Deno, Vercel Edge, Netlify Edge, and Cloudflare Workers.
bini-env is installed and configured by default in every Bini.js project. It reads env vars from the Hono request context, so variables are always resolved from the correct runtime binding — no platform-specific code needed.
getEnv(c, key) / requireEnv(c, key) read directly from the Hono request context. Zero dotenv — no .env parsing at runtime; vars come from the host platform. Vite handles .env loading during development.Quick Start
1. Register the Vite plugin
2. Read env vars in your Hono handlers
Usage Pattern
Always pass c explicitly. Cast it once at the top of the handler, then use ctx throughout.
The pattern in three steps:
Environment Prefixes
BINI_ — Client-side vars
BINI_ variables are exposed to import.meta.env. Use them for public client-side config.
VITE_ — Public client vars
VITE_ is Vite's built-in prefix. Any var starting with VITE_ is bundled into your client-side JavaScript.
No prefix — Secrets (server only)
Variables without a prefix are NOT exposed to the browser. Read them via getEnv(ctx, key) in API routes only.
Prefix Summary
| Prefix | Exposed to browser | Use for |
|---|---|---|
| BINI_ | Yes | Public client config |
| VITE_ | Yes | Public client config |
| No prefix | No | Secrets — server only |
BINI_* or VITE_* variables — both are exposed to the browser. Use un-prefixed variables for secrets and read them with getEnv(ctx, key) inside API route handlers only.Platform Support
getEnv and requireEnv delegate to Hono's env(c) adapter, which reads from the correct source on every supported platform automatically.
| Platform | Runtime | How Hono reads it |
|---|---|---|
| Node.js | Node | process.env |
| Bun | Bun | process.env |
| Vercel Edge | V8 isolate | process.env |
| Netlify Edge | Deno | Deno.env.get() |
| Cloudflare Workers | V8 isolate | CF bindings via c.env |
| Deno Deploy | Deno | Deno.env.get() |
How It Works
The biniEnv() plugin tells Vite which env prefixes to expose to import.meta.env:
On server start you will see:
Vite handles everything natively: loading .env files, watching, restarting, injecting prefixed vars, and HMR. bini-env does not reimplement any of that.
Zero dotenv: dotenv is never used at runtime. In production, vars are set in your hosting platform's environment config.
API Reference
getEnv(c, key)
Returns string | undefined.
requireEnv(c, key)
Returns string. Throws if missing.
On failure, the terminal will show:
biniEnv(options?)
| Option | Type | Default | Description |
|---|---|---|---|
| envPrefix | string | string[] | [] | Extra prefixes to expose |
Performance
| Metric | Dev | Prod |
|---|---|---|
| File reads | 0 | 0 |
| Runtime cost | ~0ms | 0 |
| Bundle impact | Minimal | Tree-shaken |
No dotenv. No disk reads. No caching layer.
Troubleshooting
| Problem | Solution |
|---|---|
| Env var undefined in production | Set variables in your hosting platform's environment dashboard. |
| Works in dev, undefined in prod | Production requires platform-level configuration. |
| Cloudflare secret not found | Secrets set via wrangler secret put are only available via c.env. Ensure you are passing c to the function. |
| TypeScript error: Context not assignable | Cast once per handler: const ctx = c as any |