bini-env

Official

Environment variable system + Vite plugin for Bini.js. Hono-native, universal runtime, zero-config dev secrets.

Overview

bini-env reads environment variables from the Hono request context, so they always resolve from the correct runtime binding - Node.js, Bun, Deno, Vercel Edge, Netlify Edge, or Cloudflare Workers - without any per-platform code.

Hono handlergetEnv(c,key)
devmirrors .env → process.envprodplatform env binding

Installation

>_Terminal
$ npm install bini-env hono
hono is a required peer dependency.

Quick Start

1. Register the plugin

vite.config.ts
import { defineConfig } from 'vite'
import { biniEnv } from 'bini-env'

export default defineConfig({
  plugins: [biniEnv()],
})

2. Read env vars in a Hono handler

src/app/api/hello.ts
import { Hono } from 'hono'
import { getEnv, requireEnv } from 'bini-env'

const app = new Hono()

app.post('/hello', async (c) => {
  try {
    const ctx = c as any

    // Throws if missing — use for required config
    const apiKey = requireEnv(ctx, 'MY_API_KEY')

    // Returns undefined if missing — use for optional config
    const appName = getEnv(ctx, 'APP_NAME') ?? 'World'

    return c.json({ message: `Hello, ${appName}!` })
  } catch (error: any) {
    if (error.message?.includes('[bini-env] Missing required')) {
      return c.json({ error: error.message }, 500)
    }
    return c.json({ error: 'Something went wrong.' }, 500)
  }
})

export default app

3. Your .env file

.env
# Server-side only
DATABASE_URL=postgres://...
STRIPE_SECRET_KEY=sk_live_...

# Exposed to the browser
BINI_API_URL=https://api.example.com
VITE_ANALYTICS_ID=UA-XXXX

4. Dev banner

>_Terminal
  Bini.js (dev)
  Environments: .env.local, .env
  Local:   http://localhost:3000/
  Network: http://192.168.1.7:3000/

Prefixes

Two prefixes are exposed to the browser: BINI_ and VITE_. Everything else stays server-side.

PrefixBrowserServer-sideUse for
No prefixNeverYesSecrets - API keys, DB URLs, tokens
BINI_AlwaysYesPublic config
VITE_AlwaysYesPublic config
The prefix list is fixed - there's no config surface that can widen what's exposed. Use no prefix for anything secret.

Platform Support

getEnv / requireEnv delegate to Hono's env(c) adapter, which reads from the correct source on every platform automatically.

PlatformRuntimeSource
Node.jsNodeprocess.env
BunBunprocess.env
Vercel EdgeV8 isolateprocess.env
Netlify EdgeDenoDeno.env.get()
Cloudflare WorkersV8 isolatec.env
Deno DeployDenoDeno.env.get()
Cloudflare: secrets set via wrangler secret put are only available via c.env inside a handler. Pass c to getEnv / requireEnv and they resolve correctly.

API Reference

getEnv(c, key)

Returns string | undefined. Use for optional config.

src/app/api/config.ts
const region = getEnv(ctx, 'AWS_REGION') ?? 'us-east-1'
const debug  = getEnv(ctx, 'DEBUG_MODE') === 'true'

requireEnv(c, key)

Returns string. Throws if the variable is missing or empty.

>_Terminal
[bini-env] error  Missing required environment variable: "SMTP_HOST"
  -> Set it in your platform's env config or hosting dashboard.

Troubleshooting

  • Works in dev, undefined in prod - .env files are only loaded by Vite during dev and preview. In production, set vars in your hosting platform's dashboard.
  • My .env value isn't taking effect in dev - either the value is already set in your shell / CI (which wins), or it's empty (KEY=), which is skipped by design.
  • requireEnv throws even though the key is in .env - an empty KEY= is treated as unset. Give it a real value, or remove the line.
  • bini-env reads .env from the wrong folder - it uses Vite's envDir, then root, then the working directory. Check envDir / root in vite.config.ts if your .env lives somewhere non-standard.
  • Cloudflare secret not found - wrangler secret put secrets only live on c.env. Pass c to getEnv / requireEnv.
  • TypeScript: Context not assignable to HonoContext - Hono 4.12+ added a symbol to HonoRequest that breaks strict assignability. Cast once per handler: const ctx = c as any.

Compatibility

ToolVersion
Vite8.x
Hono4.x
TypeScript5.x
Node.js≥ 20.19
Was this helpful?