Spring Boot-like profiles and strict env safety for Expo
npm install expo-environmentalizerA "Spring Boot-like" environment management library for Expo apps. It provides strict profile-based configuration, cascading overrides, configurable prefixes, and robust runtime type safety for any Expo command.
- ๐ก๏ธ Strict Safety: Crashes immediately if a requested profile (e.g., .env.prod) is missing. No more accidental dev-in-prod!
- ๐ Cascading Logic: .env (Base) < .env.{profile} (Override) < System Envs (Winner).
- ๐ Universal CLI: Wraps any Expo command (start, export, run:ios, prebuild) with profile injection.
- โก Lazy Evaluation: Properties are lazily evaluated to prevent crashes at import time.
- ๐ง Configurable Prefix: Use any prefix (default: EXPO_PUBLIC_), great for non-Expo projects too.
- ๐ฆ Zero-Config: No babel.config.js or metro.config.js changes required.
---
``mermaid`
graph TD
A[".env (Base defaults)"] -->|Merged| B[".env.{profile} (Profile Overrides)"]
B -->|Merged| C["Shell/System Variables (Highest Priority)"]
C -->|Injected Into| D["Expo Bundle (process.env)"]
style C fill:#d4edda,stroke:#155724
style D fill:#cce5ff,stroke:#004085
injects variables safely into the Expo ecosystem.`mermaid
sequenceDiagram
participant User
participant CLI as expo-env
participant FileSys as File System
participant Expo as Expo CLI User->>CLI: expo-env --profile=prod run:ios
CLI->>FileSys: Check for .env.prod
rect rgb(255, 240, 240)
Note right of CLI: Strict Validation
FileSys-->>CLI: Missing? -> CRASH ๐
end
FileSys-->>CLI: Exists? -> Load .env + .env.prod
CLI->>CLI: Merge Variables (Shell > Profile > Base)
CLI->>Expo: Spawn "npx expo run:ios" (with env)
Expo->>User: Native Build starts with Production Config
`$3
`mermaid
flowchart LR
subgraph CLI["CLI Layer (bin/expo-env.js)"]
A[Parse Args] --> B[Load .env Files]
B --> C[Merge with Shell Env]
C --> D[Spawn Expo]
end
subgraph Runtime["Runtime Layer (src/index.ts)"]
E[Env.Current] --> F{Lazy Getter}
G[Env.get] --> H[Return or Throw]
I[Env.getOptional] --> J[Return or Undefined]
K[Env.configure] --> L[Set Prefix]
end
CLI --> Runtime
`---
๐ฆ Installation
`bash
Using Yarn
yarn add expo-environmentalizerUsing NPM
npm install expo-environmentalizer
`---
๐ Setup & Usage
$3
Create files in your project root using the standard .env format.
.env (Base Defaults)
`env
EXPO_PUBLIC_APP_NAME="My Great App"
EXPO_PUBLIC_API_URL="http://localhost:3000"
`
.env.prod (Production Override)
`env
Overrides localhost with real API
EXPO_PUBLIC_API_URL="https://api.myapp.com"
`$3
Wrap your standard Expo commands with expo-env and specify a profile. You can use any profile name you want (dev, staging, prod, qa).`json
{
"scripts": {
"start:dev": "expo-env start --profile=dev",
"start:prod": "expo-env start --profile=prod",
"ios:dev": "expo-env run:ios --profile=dev",
"ios:prod": "expo-env run:ios --profile=prod",
"build:prod": "expo-env export --profile=prod"
}
}
`$3
Use the Env helper for type-safe access.`typescript
import { Env, AppEnvType } from 'expo-environmentalizer';// 1. Check current environment (Automatically uppercased from profile)
if (Env.Current === AppEnvType.PROD) {
console.log('๐ Running in Production Mode');
}
// 2. Use boolean helpers
if (Env.isDev) {
console.log('Debug mode enabled');
}
// 3. Get variables safely (Throws error if missing!)
const apiUrl = Env.get('API_URL');
// ^ Looks for EXPO_PUBLIC_API_URL
// 4. Get optional variables (Returns undefined if missing)
const featureFlag = Env.getOptional('BETA_FEATURE');
// 5. Get with default fallback
const timeout = Env.getOrDefault('TIMEOUT', '5000');
`---
๐ ๏ธ Advanced Features
$3
By default, this library uses
EXPO_PUBLIC_ as the prefix. You can customize this:CLI Usage:
`bash
Use a custom prefix
expo-env --profile=prod --prefix=MY_APP_ start
`Runtime Configuration:
`typescript
import { Env } from 'expo-environmentalizer';// Configure custom prefix before accessing variables
Env.configure({ prefix: 'MY_APP_' });
// Now looks for MY_APP_API_URL instead of EXPO_PUBLIC_API_URL
const apiUrl = Env.get('API_URL');
`$3
The library respects the "12-factor app" rule: System environment variables take precedence over file variables.`bash
Force a specific API key in CI, ignoring what's in .env.prod
EXPO_PUBLIC_API_KEY=ci-secret-key expo-env --profile=prod export
`$3
This library is "profile agnostic". If you run:
`bash
expo-env --profile=qa
`
It will:
1. Look for .env.qa.
2. Set Env.Current to 'QA'.
3. Load variables.You are not limited to just
dev and prod!---
๐ API Reference
$3
| Flag | Description | Default |
|------|-------------|---------|
|
--profile= | Environment profile to load | dev |
| --prefix= | Env variable prefix | EXPO_PUBLIC_ |$3
| Property/Method | Type | Description |
|----------------|------|-------------|
|
Env.Current | AppEnvType | Current environment (lazy getter) |
| Env.isDev | boolean | True if running in DEV mode |
| Env.isProd | boolean | True if running in PROD mode |
| Env.isStaging | boolean | True if running in STAGING mode |
| Env.isTest | boolean | True if running in TEST mode |
| Env.prefix | string | Current prefix |
| Env.Type | AppEnvType | Enum reference |
| Env.get(key) | (key: string) => string | Get required variable (throws if missing) |
| Env.getOptional(key) | (key: string) => string \| undefined | Get optional variable |
| Env.getOrDefault(key, default) | (key: string, default: string) => string | Get with fallback |
| Env.configure(options) | (options: EnvConfig) => void | Configure prefix |
| Env.resetConfig() | () => void | Reset to defaults |$3
`typescript
enum AppEnvType {
DEV = 'DEV',
PROD = 'PROD',
STAGING = 'STAGING',
TEST = 'TEST',
}
`---
๐งช Testing
`bash
Run all tests
npm testRun tests with coverage
npm run test:coverage
``---