React NativeExpoMobileGeolocationTutorial
IP Geolocation in React Native and Expo — Detect Country Without Exposing API Keys
ApogeoAPI5 min read
⚠️ Never put API keys in mobile apps
Anyone can extract API keys from a mobile app bundle using tools like strings or a proxy debugger.
If your API key is in a React Native/Expo bundle, it will be stolen and used by others.
Always call geolocation APIs from your own backend.
Architecture: mobile app → your backend → ApogeoAPI
React Native App
│
│ GET /api/geo (your own endpoint — no key)
▼
Your Backend (Node/Express, Next.js API route, etc.)
│
│ GET /v1/ip/:clientIp (with X-API-Key header)
▼
ApogeoAPI
Step 1: Backend proxy (Next.js API route)
// pages/api/geo.ts (or app/api/geo/route.ts for App Router)
import type { NextApiRequest, NextApiResponse } from 'next';
const API_KEY = process.env.APOGEOAPI_KEY ?? '';
const cache = new Map<string, { data: object; ts: number }>();
const TTL = 4 * 60 * 60 * 1000;
function subnet(ip: string) { return ip.split('.').slice(0, 3).join('.'); }
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
const ip =
(req.headers['x-forwarded-for'] as string)?.split(',')[0].trim() ??
req.socket.remoteAddress ??
'0.0.0.0';
const key = subnet(ip);
const hit = cache.get(key);
if (hit && Date.now() - hit.ts < TTL) {
return res.status(200).json(hit.data);
}
const apiRes = await fetch(`https://api.apogeoapi.com/v1/ip/${ip}`, {
headers: { 'X-API-Key': API_KEY },
});
const data = await apiRes.json();
cache.set(key, { data, ts: Date.now() });
res.status(200).json(data);
}
Step 2: React Native hook
// hooks/useGeo.ts
import { useState, useEffect } from 'react';
export interface GeoData {
country_code: string;
country_name: string;
city?: string;
timezone?: string;
currency?: string;
}
const CACHE_KEY = '@geo_data';
const TTL_MS = 4 * 60 * 60 * 1000;
export function useGeo() {
const [geo, setGeo] = useState<GeoData | null>(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
(async () => {
// Try AsyncStorage cache first
try {
const AsyncStorage = (await import('@react-native-async-storage/async-storage')).default;
const raw = await AsyncStorage.getItem(CACHE_KEY);
if (raw) {
const { data, ts } = JSON.parse(raw);
if (Date.now() - ts < TTL_MS) {
setGeo(data);
setLoading(false);
return;
}
}
} catch {}
// Call YOUR backend — not ApogeoAPI directly!
try {
const res = await fetch('https://yourapp.com/api/geo');
const data: GeoData = await res.json();
setGeo(data);
const AsyncStorage = (await import('@react-native-async-storage/async-storage')).default;
await AsyncStorage.setItem(CACHE_KEY, JSON.stringify({ data, ts: Date.now() }));
} catch {
setGeo({ country_code: 'US', country_name: 'United States' });
} finally {
setLoading(false);
}
})();
}, []);
return { geo, loading };
}
Step 3: Use in any component
// screens/HomeScreen.tsx
import { View, Text } from 'react-native';
import { useGeo } from '../hooks/useGeo';
const PRICE_USD = 29;
export default function HomeScreen() {
const { geo, loading } = useGeo();
if (loading) return <Text>Loading...</Text>;
const price = geo?.currency === 'EUR'
? '€26'
: geo?.currency === 'GBP'
? '£22'
: `$${PRICE_USD}`;
return (
<View>
<Text>Hello from {geo?.country_name ?? 'the world'}!</Text>
<Text>Premium: {price}/month</Text>
{geo?.country_code === 'US' && (
<Text>🇺🇸 Free shipping on orders over $50!</Text>
)}
</View>
);
}
Expo: use in an Expo Router app
// app/(tabs)/index.tsx (Expo Router v3)
import { useGeo } from '../../hooks/useGeo';
export default function Index() {
const { geo, loading } = useGeo();
// Same usage as above
}
Why not GPS instead?
GPS gives precise location but requires a location permission prompt, drains battery, and may fail indoors. IP-based country detection requires no permissions, works instantly, and is accurate enough for: currency display, content localisation, shipping zone selection, and GDPR consent detection. Use GPS only when you need precise coordinates (maps, navigation, nearby search).
Try ApogeoAPI free
1,000 requests/month forever. 14-day full-access trial. No credit card.
Get your free API key