Environment variables and bindings
Environment variables are settings passed to a program from outside rather than written in the code: the port, a secret key, the database path. This way the same code runs with different settings on your computer and on the server, and secrets stay out of Git.
The .env file
Create a .env file in the project root:
PORT=3000
JWT_SECRET=dev-secret-change-me
DB_FILE=blog.dbAdd it to .gitignore if it is not there yet. It is handy to keep a .env.example next to it with the same names but no real values — another developer will see what needs to be filled in.
Loading it in Node.js
Node.js 22 can read such a file itself with the --env-file flag. Change the scripts in package.json:
{
"scripts": {
"dev": "tsx watch --env-file=.env src/index.ts",
"build": "tsc",
"start": "node --env-file=.env dist/index.js"
}
}After restarting npm run dev, the values are available through process.env:
// src/index.ts
const port = Number(process.env.PORT ?? 3000)
serve({ fetch: app.fetch, port })The server starts on the port from .env. A variable's value is always a string, so the port is converted to a number.
The env() function
process.env exists only in Node.js. To keep route code working on any platform, Hono offers the env() function:
// src/routes/admin.ts
import { Hono } from 'hono'
import { env } from 'hono/adapter'
const admin = new Hono()
admin.get('/config', (c) => {
const { DB_FILE } = env<{ DB_FILE: string }>(c)
return c.json({ database: DB_FILE })
})
export default adminOn Node.js env(c) reads process.env, on Cloudflare Workers it reads the worker's settings, on Deno it reads Deno.env. The type in angle brackets tells TypeScript which variables to expect.
Bindings and c.env
On Cloudflare Workers, variables and connected services — a D1 database, KV storage — are called bindings and arrive in c.env. Their type is given when the app is created:
// src/index.ts
type Bindings = {
JWT_SECRET: string
}
const app = new Hono<{ Bindings: Bindings }>()
app.get('/secret-length', (c) => c.text(String(c.env.JWT_SECRET.length)))On Node.js, c.env holds the server's internal objects rather than your variables, so the course uses process.env or env(c).
| Platform | Where variables live |
|---|---|
| Node.js | process.env or env(c) |
| Bun | process.env, Bun.env or env(c) |
| Deno | Deno.env.get() or env(c) |
| Cloudflare Workers | c.env or env(c) |
Common mistake
Reading a secret at module load time with a default value:
// src/auth.ts
export const secret = process.env.JWT_SECRET ?? 'secret'If you forget to set the variable on the server, the app quietly starts with the weak key secret. Secrets must not have a default: check the variable at startup and stop the program with a clear error if it is missing.