Documentation Index Fetch the complete documentation index at: https://docs.go-mizu.dev/llms.txt
Use this file to discover all available pages before exploring further.
Environment injection allows you to pass server-side environment variables to your frontend at runtime, perfect for configuration that changes between environments.
Basic Usage
Backend Configuration
app . Use ( frontend . WithOptions ( frontend . Options {
Mode : frontend . ModeProduction ,
Root : "./dist" ,
InjectEnv : [] string { "API_URL" , "ANALYTICS_ID" , "FEATURE_FLAGS" },
}))
Frontend Access
Variables are available as window.__ENV__:
// TypeScript
declare global {
interface Window {
__ENV__ ?: Record < string , string >
}
}
const apiUrl = window . __ENV__ ?. API_URL || 'http://localhost:3000/api'
const analyticsId = window . __ENV__ ?. ANALYTICS_ID
// JavaScript
const apiUrl = window . __ENV__ ?. API_URL || '/api'
How It Works
The middleware injects a script tag into your HTML:
<! DOCTYPE html >
< html >
< head >
< title > My App </ title >
< script > window . __ENV__ = { "API_URL" : "https://api.production.com" , "ANALYTICS_ID" : "GA-123" }; </ script >
</ head >
< body >
<!-- Your app -->
</ body >
</ html >
The script is injected before </head>, making variables available before your app loads.
Use Cases
API URLs
InjectEnv : [] string { "API_URL" }
const API_URL = window . __ENV__ ?. API_URL || '/api'
export const fetchUsers = () =>
fetch ( ` ${ API_URL } /users` ). then ( r => r . json ())
Analytics
InjectEnv : [] string { "ANALYTICS_ID" , "SENTRY_DSN" }
import { init } from '@sentry/react'
if ( window . __ENV__ ?. SENTRY_DSN ) {
init ({ dsn: window . __ENV__ . SENTRY_DSN })
}
Feature Flags
InjectEnv : [] string { "FEATURES" }
const features = JSON . parse ( window . __ENV__ ?. FEATURES || '{}' )
function App () {
return (
< div >
{ features . newUI && < NewUI /> }
{ ! features . newUI && < OldUI /> }
</ div >
)
}
Multi-Environment Setup
func getEnvVars ( env string ) [] string {
vars := [] string { "API_URL" }
if env == "production" {
vars = append ( vars , "ANALYTICS_ID" , "SENTRY_DSN" )
}
if env == "development" {
vars = append ( vars , "DEBUG" , "MOCK_API" )
}
return vars
}
app . Use ( frontend . WithOptions ( frontend . Options {
InjectEnv : getEnvVars ( os . Getenv ( "ENV" )),
}))
Security Considerations
Never Inject Secrets
// ❌ DON'T inject sensitive data
InjectEnv : [] string {
"DATABASE_PASSWORD" , // Visible to users!
"SECRET_KEY" , // Visible to users!
"PRIVATE_API_KEY" , // Visible to users!
}
Why: All injected variables are visible in the HTML source and browser console.
Only Inject Public Configuration
// ✅ DO inject public config
InjectEnv : [] string {
"API_URL" , // Public endpoint
"ANALYTICS_ID" , // Public tracking ID
"APP_VERSION" , // Public version
"ENVIRONMENT" , // Public environment name
}
Prefixing Convention
Follow the convention of prefixing public variables:
# .env file
VITE_API_URL = https://api.example.com # Public (VITE_ prefix)
VITE_ANALYTICS_ID = GA-123 # Public
DATABASE_URL = postgresql://... # Private (no prefix)
SECRET_KEY = abc123 # Private (no prefix)
// Only inject VITE_ prefixed vars
InjectEnv : getPublicEnvVars (),
func getPublicEnvVars () [] string {
var publicVars [] string
for _ , env := range os . Environ () {
if strings . HasPrefix ( env , "VITE_" ) {
key := strings . Split ( env , "=" )[ 0 ]
publicVars = append ( publicVars , key )
}
}
return publicVars
}
Build-Time vs Runtime
Build-Time (Vite/Webpack)
// Build-time replacement
const apiUrl = import . meta . env . VITE_API_URL
Pros:
Type-safe (TypeScript)
Tree-shaking works
Optimizations apply
Cons:
Must rebuild for each environment
Can’t change without rebuild
Runtime (Mizu Injection)
// Runtime injection
const apiUrl = window . __ENV__ ?. API_URL
Pros:
Single build for all environments
Change without rebuild
Dynamic configuration
Cons:
Not type-safe by default
Available after page load
Requires fallbacks
Hybrid Approach
Best of both worlds:
// Use build-time for development, runtime for production
const apiUrl =
window . __ENV__ ?. API_URL || // Runtime (production)
import . meta . env . VITE_API_URL || // Build-time (development)
'http://localhost:3000/api' // Fallback
TypeScript Support
Create type definitions:
// src/env.d.ts
interface ImportMetaEnv {
readonly VITE_API_URL : string
readonly VITE_ANALYTICS_ID : string
}
interface ImportMeta {
readonly env : ImportMetaEnv
}
// Runtime env
declare global {
interface Window {
__ENV__ ?: {
API_URL ?: string
ANALYTICS_ID ?: string
SENTRY_DSN ?: string
}
}
}
export {}
Use safely:
const config = {
apiUrl: window . __ENV__ ?. API_URL || '/api' ,
analyticsId: window . __ENV__ ?. ANALYTICS_ID ,
}
// Type-safe
const url : string = config . apiUrl
Testing
Mock in Tests
// test-utils.tsx
beforeEach (() => {
window . __ENV__ = {
API_URL: 'http://localhost:3001/api' ,
ANALYTICS_ID: 'test-analytics' ,
}
})
afterEach (() => {
delete window . __ENV__
})
Environment-Specific Tests
describe ( 'Production config' , () => {
beforeEach (() => {
window . __ENV__ = {
API_URL: 'https://api.production.com' ,
}
})
it ( 'uses production API' , () => {
expect ( getApiUrl ()). toBe ( 'https://api.production.com' )
})
})
You can also inject config via meta tags:
app . Use ( frontend . WithOptions ( frontend . Options {
InjectMeta : map [ string ] string {
"api-url" : os . Getenv ( "API_URL" ),
"analytics-id" : os . Getenv ( "ANALYTICS_ID" ),
},
}))
Read in frontend:
const apiUrl = document . querySelector ( 'meta[name="api-url"]' )?. getAttribute ( 'content' )
When to use:
SEO-relevant config
Prefer meta tags over script tags
Framework requires meta tags
Next Steps
Configuration All configuration options
Security Security best practices
Deployment Build and deployment guide