DEV Community

Kamran Khan
Kamran Khan

Posted on

One env var between localhost and production: how VITE_API_URL keeps my axios client deploy-proof

I once shipped a frontend with http://localhost:8000/api hardcoded in the axios client. It worked perfectly — on my machine. The production build called localhost on every user's browser, failed silently, and I spent an evening learning about CORS before realizing the actual bug was a string literal.

The rule now: the API base URL comes from exactly one env var, with a sane fallback. Nothing else in the frontend knows the backend's address.

// .env.example
VITE_API_URL=http://localhost:8000
Enter fullscreen mode Exit fullscreen mode
const client = isDemoMode() ? createDemoClient() : axios.create({
  baseURL: `${import.meta.env.VITE_API_URL || 'http://localhost:8000'}/api`,
  headers: {
    Accept: 'application/json',
  },
});
Enter fullscreen mode Exit fullscreen mode

Three decisions packed into that one line:

  1. import.meta.env.VITE_API_URL is the source of truth. Every environment — local dev, staging, production — sets its own value. The frontend code never changes between deploys; only the env file does.

  2. The || 'http://localhost:8000' fallback means a fresh clone still runs. Nobody should have to create .env before the app boots — a new developer (or me, six months later) should be able to npm run dev and see the login page hitting the local API immediately. The fallback is localhost because that's where the backend runs by default.

  3. /api is appended by the client, not the env var. The env var names the host, the client names the prefix. If I ever version the API (/api/v2), I change it in one place, not in every deployment's env file.

The .env.example in the repo documents both knobs — VITE_API_URL and VITE_DEMO_MODE — with comments explaining what each does and what the defaults assume. The real .env is gitignored; the example is the contract. Anyone cloning the repo knows exactly which variables exist and which are optional.

One Vite-specific gotcha for Laravel devs used to .env files on the backend: only variables prefixed with VITE_ are exposed to the frontend. API_URL alone won't work — import.meta.env.API_URL will be undefined and your fallback will silently take over everywhere, including production. Name it VITE_API_URL from the start and you'll never chase that ghost.

The demo-mode branch above (isDemoMode() ? createDemoClient() : ...) is a separate story — it's what lets the public demo run with no backend at all. But the env-var pattern is what makes the real deployments boring, and boring deployments are the goal.

Small discipline, big payoff — it's one of the patterns baked into my Laravel + React SaaS Starter Kit: https://kamranofficial.gumroad.com/l/mhekoig — $19 one-time.

Top comments (0)