2HAASStart a project
All posts

Tech Tips

Environment variables in Node.js and Vite

Hard-coded URLs and keys make an app fragile and unsafe. How .env files work in Node.js and Vite, which values reach the browser, and how to validate them at startup.

The 2HAAS team2 min read

An application runs in several places: your laptop, a staging server, production. The code is the same in each. The database address, the API URL and the keys are not. Environment variables keep those differences out of the code, so the same build can run anywhere.

In Node.js

Put local values in a .env file at the project root.

DATABASE_URL=postgres://localhost:5432/shop
PORT=3000

Current Node.js versions can load the file without a library:

node --env-file=.env server.js

On older versions, the dotenv package does the same job with import "dotenv/config" at the top of the entry file. Either way, the values appear on process.env:

const port = Number(process.env.PORT ?? 3000);

In Vite

Vite reads .env files by itself and picks the right one for the current mode.

# .env.development
VITE_API_URL=http://localhost:3000/api

# .env.production
VITE_API_URL=https://api.example.com
const res = await fetch(`${import.meta.env.VITE_API_URL}/products`);

npm run dev uses the development file and npm run build uses the production one. The files are loaded in this order, with later ones winning:

File Loaded Commit it?
.env Always Yes, if it holds no secrets
.env.local Always No
.env.[mode] Only in that mode Yes, if it holds no secrets
.env.[mode].local Only in that mode No

The rule that prevents leaks

Vite exposes only variables that start with VITE_, and it writes their values into the JavaScript bundle. Next.js does the same with NEXT_PUBLIC_.

So a prefixed variable is public. Anyone can read it in the browser. An API URL is fine there. A secret key is not. Secrets belong in server code only, as described in keeping API keys out of your frontend.

Validate at startup

A missing variable should stop the app immediately with a clear message. It should not cause a strange error three hours later.

const required = ["DATABASE_URL", "SESSION_SECRET"];
const missing = required.filter((name) => !process.env[name]);

if (missing.length > 0) {
  throw new Error(`Missing environment variables: ${missing.join(", ")}`);
}

For anything larger, describe the configuration with a schema library and parse process.env once. You get types and validation together.

Keep them out of Git

.env
.env.local
.env.*.local

Add those lines to .gitignore, and commit a .env.example that lists every variable with a placeholder. New developers then know what to fill in.

In production

Do not copy .env files to servers or bake them into images. Set the values in your hosting platform's settings or a secret manager. They are injected when the app starts, can be changed without a new build, and stay out of the repository.

A short checklist

  1. No URL, key or password is hard-coded.
  2. Only values that may be public carry a public prefix.
  3. The app refuses to start when a required variable is missing.
  4. .env files with real values are ignored by Git.
  5. .env.example is up to date.

Keep reading

  1. Tech Tips2 min read

    Async error handling in JavaScript: try/catch done right

    Unhandled promise rejections crash Node.js services and leave browser apps quietly broken. Catch async errors with try/catch, .catch and a global safety net.

  2. Tech Tips2 min read

    Dependency hygiene: audit, update and prune npm packages

    Outdated and unused packages are a security risk and a tax on every install. A short routine with npm audit, npm outdated and a cleanup tool keeps a project healthy.

  3. Tech Tips2 min read

    Refactoring with AI assistants: a safe workflow

    AI assistants can untangle a long function in seconds, and introduce a subtle bug just as fast. This workflow keeps the speed and puts tests and review in charge.

Tell us what’s in the way.

A few lines is enough. You get a candid yes, a candid no, or a sharper question.

  • Working software in week four
  • A reply in one business day
  • Handover is the deliverable

Prefer email? [email protected]

The problem, who it is for, and what is in the way right now.

One business day to a real reply. No sales sequence, no call required.