env-exposure

How Environment Exposure Works

When a browser requests /env.js, servejs serves a small JavaScript snippet containing selected environment variables.

  • Source of truth: only variables from the host environment with the JSENV_ prefix are considered (e.g., JSENV_API_URL).
  • Snapshot timing: variables are snapshotted when the handler is constructed and remain constant until you recreate the handler or restart the process; env.js is not regenerated per-request with fresh OS env reads.
  • Allowlist: you can optionally allowlist specific variable names (without the JSENV_ prefix), e.g., WithEnvAllowlist("API_URL", "FEATURE_FLAG") will include JSENV_API_URL and JSENV_FEATURE_FLAG.
  • Exposure modes: variables can be exposed as individual global assignments, as a single object, or both:
    • Global assignments (default): window.NAME = "value" for each name
    • Object: window.__ENV__ = { NAME: "value" } (object name is configurable via WithEnvObjectName)
    • Both: emits both forms

Tip: If you use the object mode, you may optionally freeze it in your app code to avoid accidental mutation, e.g., Object.freeze(window.__ENV__).

Using variables in JavaScript

First, make sure your HTML references the generated script:

1
<script src="/env.js"></script>

Then, depending on your exposure mode:

  • Global assignments (default):

    1
    2
    3
    4
    5
    6
    7
    
    <script>
    	// If you set JSENV_API_URL in the server environment:
    	// window.API_URL is now available
    	fetch(`${window.API_URL}/status`)
    		.then(r => r.json())
    		.then(console.log);
    </script>
  • Object mode (default object name ENV):

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    
    <script>
    	// Access via window.__ENV__
    	const { API_URL, FEATURE_FLAG } = window.__ENV__;
    	// Optional: freeze to prevent accidental mutation in app code
    	Object.freeze(window.__ENV__);
    
    	fetch(`${API_URL}/status`)
    		.then(r => r.json())
    		.then(console.log);
    </script>

If you configured a custom object name (e.g., WithEnvObjectName("ENV")), use window.ENV instead of window.__ENV__.

Tip (TypeScript): add a minimal global type so window.__ENV__ is typed in your project.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
// env.d.ts
declare global {
	interface Window {
		__ENV__?: {
			API_URL?: string;
			FEATURE_FLAG?: string;
			// add the keys you use
		};
	}
}
export {};