BizFirst Deployment Models V2
Chapter 2 How It Works in BizFirst

The .env Files

FlowStudio's app root (apps/flow-studio/) contains three env files, each with a distinct role:

.env.example

Checked into source control as documentation — every supported key, with comments explaining what it does and what happens when it's left empty. Not read by Vite itself.

.env.local

A developer's personal local overrides, gitignored. Read by both vite dev and any local vite build.

.env.production

Values used when building with --mode production — the file that matters for a real deployment build.

The Keys

KeyRequired?Purpose
VITE_API_BASE_URL Optional Base URL of the API server. Resolved through calculateApiUrl() — see Chapter 2.4.
VITE_TENANT_ID Required Numeric tenant ID for this deployment. FlowStudio fails fast at startup if this is missing or not a positive number.
VITE_GOOGLE_CLIENT_ID Optional Google OAuth client ID. Leave empty to disable Google Sign-In.
VITE_API_TIMEOUT Optional API request timeout in milliseconds. Defaults to 30000.
VITE_BASE_URL Optional App base path for reverse-proxy deployments (e.g. /flowstudio/). Defaults to /.
VITE_FORM_STUDIO_DEV_URL Optional Form Studio URL when running on localhost. Defaults to http://localhost:5175.
VITE_FORM_STUDIO_PATH Optional Form Studio path when deployed, appended to window.location.origin. Defaults to /formstudio/.

How the Code Consumes Them

Two entry points read these values and turn them into the running app's configuration: main.tsx and App.tsx.

main.tsximport { calculateApiUrl } from '@bizfirst/common-js';

const API_BASE_URL = calculateApiUrl(import.meta.env.VITE_API_BASE_URL);
const TENANT_ID = parseInt(import.meta.env.VITE_TENANT_ID ?? '1', 10);
const GOOGLE_CLIENT_ID = import.meta.env.VITE_GOOGLE_CLIENT_ID || '';
App.tsx — getConfig()function getConfig() {
  const apiBaseUrl = calculateApiUrl(import.meta.env.VITE_API_BASE_URL);
  const tenantIdStr = import.meta.env.VITE_TENANT_ID;

  const tenantId = parseInt(tenantIdStr ?? '1', 10);
  if (isNaN(tenantId) || tenantId <= 0) {
    throw new Error(
      'Invalid VITE_TENANT_ID: must be a positive number\n' +
      `Received: "${tenantIdStr}"`
    );
  }
  return { apiBaseUrl, tenantId };
}

Note what App.tsx does not do: it no longer throws if VITE_API_BASE_URL is missing. Only VITE_TENANT_ID fails fast. That's a deliberate consequence of calculateApiUrl() always returning a usable value — an empty base URL is a valid, common configuration (the inline deployment case), not an error condition.