Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions PR_DOCUMENTATION.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# PR Documentation

## Issue

**Replace hardcoded mobile API hosts with env-driven runtime configuration** (#199)

## Summary

This PR introduces environment-driven runtime configuration for the mobile app API base URL, removing hardcoded localhost assumptions and enabling contributors to target the correct backend without code changes.

## What this PR addresses

- Hardcoded mobile API host values that only work on local emulators or a specific development setup
- Runtime configuration for both development and physical device scenarios
- Clear validation and failure behavior when configuration is invalid or missing

## Acceptance Criteria

- API base URLs are sourced from environment-aware configuration
- Development and device setup behavior is documented
- Invalid configuration fails clearly and predictably

## Expected changes

- A mobile runtime config layer that reads the API base URL from environment variables or platform-specific runtime settings
- Support for distinguishing between emulator/local host and device-hosted backend endpoints
- Documentation updates in `veilend-mobile/README.md` or related onboarding docs describing how to configure the mobile app at runtime
- Validation logic that surfaces misconfiguration during startup instead of silently failing

## Validation

- Verify the app uses the configured API host at runtime instead of hardcoded values
- Confirm the mobile app can connect to backend services on both emulator and device environments when configured correctly
- Check that invalid or missing configuration produces a clear error message and prevents ambiguous failures

## Notes

The mobile API host configuration change aligns with the current mobile architecture goal of making VeilLend easy to run in contributor environments while preserving Stellar-native privacy-first UX.
9 changes: 9 additions & 0 deletions veilend-mobile/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,15 @@
# Copy this file to `.env` and fill values as needed for your environment.

# Backend API endpoints
# For web / Expo web, use a localhost endpoint or a running local backend.
# Example for local web development:
# API_URL_WEB=http://localhost:3000
# For Android emulator, use the Android emulator loopback address:
# API_URL_MOBILE=http://10.0.2.2:3000
# For iOS simulator, use localhost:
# API_URL_MOBILE=http://localhost:3000
# For a physical device, use a machine IP accessible on the same network:
# API_URL_MOBILE=http://192.168.1.100:3000
API_URL_WEB=http://localhost:3000
API_URL_MOBILE=http://10.0.2.2:3000

Expand Down
45 changes: 45 additions & 0 deletions veilend-mobile/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# VeilLend Mobile Runtime Configuration

The VeilLend mobile app now uses runtime configuration for backend API hosts instead of hardcoded localhost values.

## Supported Environment Variables

- `API_URL_WEB` — backend URL used when running on web or Expo web.
- `API_URL_MOBILE` — backend URL used when running in mobile emulators and on devices.

## Setup

1. Copy the example environment file:

```bash
cd veilend-mobile
cp .env.example .env
```

2. Set the backend endpoints for your environment.

### Example values

- Web development: `API_URL_WEB=http://localhost:3000`
- Android emulator: `API_URL_MOBILE=http://10.0.2.2:3000`
- iOS simulator: `API_URL_MOBILE=http://localhost:3000`
- Physical device: `API_URL_MOBILE=http://<YOUR_MACHINE_IP>:3000`

## Device scenarios

- **Android emulator:** use `10.0.2.2` to reach the host machine from the emulator.
- **iOS simulator:** use `localhost` because the simulator shares the host network.
- **Physical device:** use your computer's LAN IP address (for example `192.168.1.100`) so the device can reach the backend.

## Validation

The app validates these values at startup and will fail clearly if a required configuration is missing or invalid.

- Missing `API_URL_WEB` or `API_URL_MOBILE` will surface a startup error.
- Invalid URLs will be rejected with a descriptive message.

## Runtime config flow

- `app.config.ts` reads `API_URL_WEB` and `API_URL_MOBILE` from the environment and injects them into Expo `extra`.
- `src/utils/config.ts` resolves the active backend URL for the current platform.
- `src/utils/api.ts` uses the resolved runtime API host as the Axios `baseURL`.
20 changes: 20 additions & 0 deletions veilend-mobile/app.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import 'dotenv/config';

const requiredEnv = (key: string): string => {
const value = process.env[key]?.trim();
if (!value) {
throw new Error(
`Missing required environment variable ${key}. Copy veilend-mobile/.env.example to veilend-mobile/.env and set ${key}.`
);
}
return value;
};

export default ({ config }: any) => ({
...config,
extra: {
...config.extra,
API_URL_WEB: requiredEnv('API_URL_WEB'),
API_URL_MOBILE: requiredEnv('API_URL_MOBILE'),
},
});
46 changes: 42 additions & 4 deletions veilend-mobile/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions veilend-mobile/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
"devDependencies": {
"@types/react": "~19.1.10",
"babel-preset-expo": "^54.0.10",
"dotenv": "^16.1.4",
"postcss": "^8.5.6",
"tailwindcss": "^3.3.2",
"tsx": "^4.22.4",
Expand Down
6 changes: 2 additions & 4 deletions veilend-mobile/src/utils/api.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
import axios from 'axios';
import { useStore } from '../store/store';
import { Platform } from 'react-native';
import { reportError } from './errorReporting';
import { getApiBaseUrl } from './config';

const API_URL = Platform.OS === 'web'
? 'http://localhost:3000'
: 'http://10.0.2.2:3000';
const API_URL = getApiBaseUrl();

const api = axios.create({
baseURL: API_URL,
Expand Down
48 changes: 48 additions & 0 deletions veilend-mobile/src/utils/config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import Constants from 'expo-constants';
import { Platform } from 'react-native';

const CONFIG_KEYS = {
web: 'API_URL_WEB',
default: 'API_URL_MOBILE',
};

function getRawConfigValue(key: string): string | undefined {
const extraConfig = (Constants.expoConfig?.extra ?? {}) as Record<string, unknown>;
const extraValue = extraConfig[key];

if (typeof extraValue === 'string' && extraValue.trim()) {
return extraValue.trim();
}

const envValue = process.env[key];
if (typeof envValue === 'string' && envValue.trim()) {
return envValue.trim();
}

return undefined;
}

function validateUrl(url: string, key: string): string {
try {
const parsed = new URL(url);
if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') {
throw new Error();
}
return url;
} catch {
throw new Error(`Invalid ${key} value: ${url}. Use a valid http:// or https:// URL.`);
}
}

export function getApiBaseUrl(): string {
const key = Platform.OS === 'web' ? CONFIG_KEYS.web : CONFIG_KEYS.default;
const apiUrl = getRawConfigValue(key);

if (!apiUrl) {
throw new Error(
`${key} is not configured. Set ${key} in veilend-mobile/.env or via your EAS/build environment variables.`
);
}

return validateUrl(apiUrl, key);
}
Loading