rn-dev-url
v0.1.0
Published
Automatically resolves the correct API base URL for React Native / Expo apps in development
Downloads
9
Maintainers
Readme
rn-dev-url
Automatically resolves the correct API base URL for React Native / Expo apps in development. Makes dev API resolution work automatically when using a local backend, while never exposing LAN IPs or performing smart detection in production.
Features
- ✅ Zero native dependencies - Pure TypeScript/JavaScript
- ✅ Works everywhere - Expo Go, Expo Dev Client, and Bare React Native
- ✅ Production-safe - Never runs smart detection or exposes LAN IPs in production
- ✅ Predictable behavior - Clear rules, no hidden magic
- ✅ Small bundle size - Minimal footprint
Installation
pnpm add rn-dev-url
# or
npm install rn-dev-url
# or
yarn add rn-dev-urlQuick Start
import { getDevApiUrl } from 'rn-dev-url';
// In your API configuration
const apiConfig = await getDevApiUrl(3000, {
prodUrl: 'https://api.example.com',
healthPath: '/health', // optional
});
console.log(apiConfig.url); // http://10.0.2.2:3000 (Android emulator)
console.log(apiConfig.source); // 'android-emulator'API Reference
getDevApiUrl(port, options?)
Returns the appropriate API base URL for the current environment.
Parameters:
port(number) - The port number of your local backendoptions(object, optional):prodUrl(string, optional) - Required in production, optional in dev. The production/remote API URL.healthPath(string, optional) - Optional path to test connectivity (e.g., '/health'). If provided, will ping each candidate.preferLan(boolean, optional) - If true, try LAN IP before simulator defaults. Default:false.forceProdUrlInDev(boolean, optional) - If true, skip smart detection in dev and immediately use prodUrl. Default:false.log(boolean, optional) - Enable developer logs. Default:false.timeoutMs(number, optional) - Connectivity timeout in milliseconds. Default:2500.
Returns:
Promise<{
url: string;
source: 'prod' | 'android-emulator' | 'ios-simulator' | 'lan-device' | 'fallback';
}>Behavior
Production Mode
Condition: __DEV__ === false or NODE_ENV === 'production'
Behavior:
- ✅ Smart detection is DISABLED
- ✅ LAN IP evaluation is DISABLED
- ✅
prodUrlis REQUIRED (throws if missing) - ✅ Returns
prodUrlimmediately
Example:
// Production build
const config = await getDevApiUrl(3000, {
prodUrl: 'https://api.example.com',
});
// Returns: { url: 'https://api.example.com', source: 'prod' }Dev Mode - Default (Local Backend)
Condition: __DEV__ === true and forceProdUrlInDev !== true
Behavior:
- Tries candidates in this order:
- Android emulator →
http://10.0.2.2:<port> - iOS simulator →
http://127.0.0.1:<port> - LAN device →
http://<lan-ip>:<port>
- Android emulator →
- If
preferLan === true, LAN is tried before simulator defaults - If
healthPathprovided, tests connectivity for each candidate - Returns first reachable URL
- Falls back to
prodUrlif no local backend reachable - Returns empty URL if no candidate and no
prodUrl
Example:
// Development with local backend
const config = await getDevApiUrl(3000, {
prodUrl: 'https://api.example.com', // fallback
healthPath: '/health',
});
// Returns: { url: 'http://10.0.2.2:3000', source: 'android-emulator' }Dev Mode - Remote API
Condition: __DEV__ === true and forceProdUrlInDev === true
Behavior:
- ✅ Skips smart detection entirely
- ✅ Immediately returns
prodUrl - ✅
prodUrlis REQUIRED (throws if missing)
Use cases:
- Frontend-only developers
- Remote dev / staging APIs
- Shared backend environments
- CI preview builds
Example:
// Development with remote dev API
const config = await getDevApiUrl(3000, {
prodUrl: 'https://dev-api.example.com',
forceProdUrlInDev: true,
});
// Returns: { url: 'https://dev-api.example.com', source: 'prod' }Behavior Table
| Mode | prodUrl | forceProdUrlInDev | Behavior |
|------|-----------|---------------------|----------|
| Production | ✅ Required | N/A | Returns prodUrl, no detection |
| Production | ❌ Missing | N/A | Throws error |
| Dev (default) | Optional | false | Tries local candidates, falls back to prodUrl |
| Dev (default) | ❌ Missing | false | Tries local candidates, returns empty URL if none reachable |
| Dev (remote) | ✅ Required | true | Returns prodUrl, skips detection |
| Dev (remote) | ❌ Missing | true | Throws error |
Safety Guardrails
The library guarantees:
- ✅ Never exposes LAN IPs in production - Smart detection is completely disabled
- ✅ Never runs smart detection in production - Hard-coded check prevents execution
- ✅ Fails predictably - Clear error messages when required options are missing
- ✅ No hidden magic - All behavior is explicit and documented
Troubleshooting
LAN IP Not Detected
LAN detection is best-effort only. If automatic detection fails:
Set explicit override:
export RN_DEV_HOST_IP=192.168.1.100Or use
preferLanoption:await getDevApiUrl(3000, { preferLan: true, // Will try LAN first if detected });
Android Emulator Not Reaching Backend
If 10.0.2.2 doesn't work, try using adb reverse:
adb reverse tcp:3000 tcp:3000Then use localhost or 127.0.0.1 instead of the emulator IP.
Connectivity Checks Timing Out
If health checks are too slow:
Increase timeout:
await getDevApiUrl(3000, { healthPath: '/health', timeoutMs: 5000, // 5 seconds });Or skip health checks:
await getDevApiUrl(3000, { // No healthPath - uses first candidate });
Using Remote Dev API
If your team uses a shared remote dev API:
await getDevApiUrl(3000, {
prodUrl: 'https://dev-api.example.com',
forceProdUrlInDev: true, // Skip local detection
});Examples
Basic Usage
import { getDevApiUrl } from 'rn-dev-url';
const { url } = await getDevApiUrl(3000, {
prodUrl: 'https://api.example.com',
});
const api = axios.create({ baseURL: url });With Health Check
const { url, source } = await getDevApiUrl(3000, {
prodUrl: 'https://api.example.com',
healthPath: '/health',
log: true, // See which URL was selected
});
console.log(`Using ${source}: ${url}`);Prefer LAN IP
const { url } = await getDevApiUrl(3000, {
prodUrl: 'https://api.example.com',
preferLan: true, // Try LAN before emulator/simulator
healthPath: '/health',
});Remote Dev API
const { url } = await getDevApiUrl(3000, {
prodUrl: 'https://dev-api.example.com',
forceProdUrlInDev: true, // Always use prodUrl in dev
});How It Works
- Platform Detection: Detects Android emulator, iOS simulator, or physical device
- Candidate Generation: Creates URL candidates based on platform
- Connectivity Testing (optional): Pings health endpoint to verify reachability
- Selection: Returns first reachable candidate, or falls back to
prodUrl
Non-Goals
This library does NOT implement:
- ❌ Proxying
- ❌ Port forwarding
- ❌ Tunnels
- ❌ Automatic
adb reverse - ❌ IPv6 support
- ❌ Network scanning
The library prioritizes predictability and safety over clever features.
License
MIT
