npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

rn-dev-url

v0.1.0

Published

Automatically resolves the correct API base URL for React Native / Expo apps in development

Downloads

9

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-url

Quick 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 backend
  • options (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
  • prodUrl is REQUIRED (throws if missing)
  • ✅ Returns prodUrl immediately

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:
    1. Android emulator → http://10.0.2.2:<port>
    2. iOS simulator → http://127.0.0.1:<port>
    3. LAN device → http://<lan-ip>:<port>
  • If preferLan === true, LAN is tried before simulator defaults
  • If healthPath provided, tests connectivity for each candidate
  • Returns first reachable URL
  • Falls back to prodUrl if 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
  • prodUrl is 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:

  1. Never exposes LAN IPs in production - Smart detection is completely disabled
  2. Never runs smart detection in production - Hard-coded check prevents execution
  3. Fails predictably - Clear error messages when required options are missing
  4. No hidden magic - All behavior is explicit and documented

Troubleshooting

LAN IP Not Detected

LAN detection is best-effort only. If automatic detection fails:

  1. Set explicit override:

    export RN_DEV_HOST_IP=192.168.1.100
  2. Or use preferLan option:

    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:3000

Then use localhost or 127.0.0.1 instead of the emulator IP.

Connectivity Checks Timing Out

If health checks are too slow:

  1. Increase timeout:

    await getDevApiUrl(3000, {
      healthPath: '/health',
      timeoutMs: 5000, // 5 seconds
    });
  2. 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

  1. Platform Detection: Detects Android emulator, iOS simulator, or physical device
  2. Candidate Generation: Creates URL candidates based on platform
  3. Connectivity Testing (optional): Pings health endpoint to verify reachability
  4. 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