Debugging Network Requests in React Native: Fetch, Axios, and DevTools

Network bugs in React Native usually fall into a few categories: the request never leaves the device, the server rejects it, the response shape is different from what the app expects, or the UI keeps using stale data. The fastest way to debug them is to inspect the request, log safely, reproduce on the platform that fails, and verify the server-side trace.
Quick Answer
Start with React Native DevTools when your stack supports the Network panel. It records requests made through fetch, XMLHttpRequest, and images. Add a thin logging layer around Fetch or Axios, redact secrets, capture status codes and request IDs, cancel stale requests with AbortController, and compare device logs with backend logs. Use Android Studio, Xcode, or a proxy tool when the issue is TLS, cleartext HTTP, certificates, localhost routing, or platform networking.
For implementation patterns, pair this guide with React Native REST API integration, React Native memory leak fixes, and the current Instamobile React Native stack.
Use the Right Debugging Tool
Use this order for most API bugs:
- React Native DevTools Network panel for supported
fetch,XMLHttpRequest, and image requests. - Application logs from a single API wrapper or Axios instance.
- Backend logs using a request ID or correlation ID.
- Android Studio Logcat or Xcode Console for platform-level failures.
- A proxy tool such as Charles or Proxyman when you need full device traffic inspection and your security policy allows it.
Flipper can still be useful in older projects that already depend on it, but it should not be the default recommendation for modern React Native network debugging.
Log Requests Without Leaking Secrets
Never log access tokens, refresh tokens, API keys, passwords, payment payloads, or PII. A useful debug log only needs method, path, status, duration, and a request ID.
type RequestLog = {
method: string;
url: string;
status?: number;
durationMs: number;
requestId?: string;
};
function logNetworkEvent(event: RequestLog) {
if (__DEV__) {
console.log('[network]', event);
}
}
When the backend returns a request ID, surface it in error logs and support screens. It lets developers match the exact mobile request to server logs without exposing user data.
Mega Bundle Sale is ON! Get ALL of our React Native codebases at 90% OFF discount 🔥
Get the Mega BundleDebug Fetch With Timeouts and Cancellation
React Native supports the familiar Fetch API. Build a small wrapper so every request has the same timeout, error shape, and logging behavior.
type ApiErrorBody = {
message?: string;
code?: string;
};
export class ApiError extends Error {
status: number;
body: ApiErrorBody | null;
constructor(status: number, body: ApiErrorBody | null) {
super(body?.message ?? `Request failed with status ${status}`);
this.status = status;
this.body = body;
}
}
export async function fetchJson<T>(
url: string,
options: RequestInit & { timeoutMs?: number } = {},
): Promise<T> {
const startedAt = Date.now();
const controller = new AbortController();
const timeoutId = setTimeout(
() => controller.abort(),
options.timeoutMs ?? 15_000,
);
try {
const response = await fetch(url, {
...options,
signal: controller.signal,
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
...options.headers,
},
});
const requestId = response.headers.get('x-request-id') ?? undefined;
const text = await response.text();
const body = text ? JSON.parse(text) : null;
logNetworkEvent({
method: options.method ?? 'GET',
url,
status: response.status,
durationMs: Date.now() - startedAt,
requestId,
});
if (!response.ok) {
throw new ApiError(response.status, body);
}
return body as T;
} finally {
clearTimeout(timeoutId);
}
}
Use cancellation when a user leaves the screen, changes a search term, or starts a newer request that makes the old response irrelevant.
Debug Axios With One Shared Instance
Axios is still useful when a project relies on interceptors, upload progress, response transforms, or shared error handling. Keep all Axios behavior in one module.
import axios, { AxiosError } from 'axios';
export const api = axios.create({
baseURL: 'https://api.example.com',
timeout: 15_000,
});
api.interceptors.request.use(config => {
config.headers.set?.('x-client', 'mobile');
config.metadata = { startedAt: Date.now() };
return config;
});
api.interceptors.response.use(
response => {
const startedAt = response.config.metadata?.startedAt ?? Date.now();
logNetworkEvent({
method: response.config.method?.toUpperCase() ?? 'GET',
url: response.config.url ?? '',
status: response.status,
durationMs: Date.now() - startedAt,
requestId: response.headers['x-request-id'],
});
return response;
},
(error: AxiosError) => {
const status = error.response?.status;
logNetworkEvent({
method: error.config?.method?.toUpperCase() ?? 'GET',
url: error.config?.url ?? '',
status,
durationMs: Date.now() - (error.config?.metadata?.startedAt ?? Date.now()),
requestId: error.response?.headers?.['x-request-id'],
});
return Promise.reject(error);
},
);
If TypeScript complains about metadata, define a small Axios module augmentation in your app. Keep the augmentation near the API client so it does not leak into unrelated code.
Triage Common React Native Network Failures
Use symptoms to narrow the issue:
| Symptom | Likely cause | What to check |
|---|---|---|
Network request failed on Android only | TLS chain, cleartext HTTP, emulator routing, certificate issue | Use HTTPS, verify full certificate chain, check Android network security config |
| Works on simulator but not device | localhost, Wi-Fi, firewall, backend binding | Use the machine LAN IP or a tunnel; verify the server accepts device traffic |
401 or 403 | expired token, wrong audience, missing role, clock skew | Inspect auth headers without logging token values; compare backend auth logs |
404 | wrong base URL, route prefix, API version | Log normalized URL and environment name |
| Request repeats too often | focus refetch, effect dependencies, retry loop | Add request IDs, inspect component lifecycle, use a server-state cache |
| UI shows old data | stale cache or optimistic update bug | Invalidate the correct query/list and update pagination state |
| Upload fails on one platform | MIME type, file URI, permissions, multipart boundary | Log file metadata and test a small known-good file |
React Native apps do not run inside a browser sandbox, so browser CORS rules are not usually the first suspect. If you see a CORS-like failure, also verify TLS, redirects, API gateway rules, and custom origin checks on the server.
Test Network Behavior Explicitly
Before shipping API-heavy changes:
- test on iOS simulator, Android emulator, and at least one physical device;
- test Wi-Fi, cellular, airplane mode, and slow network conditions;
- test token expiry and refresh;
- test empty, loading, error, and retry states;
- test app background and foreground resume;
- confirm that canceled requests do not update unmounted screens;
- verify backend logs include the same request IDs seen in the app.
For release readiness, use the Instamobile publishing checklist.
Useful Links
- React Native Networking
- React Native DevTools
- Axios repository and docs
- AbortSignal on MDN
- Chrome DevTools Network panel
- Instamobile REST API integration guide
Looking for a custom mobile application?
Our team of expert mobile developers can help you build a custom mobile app that meets your specific needs.
Get in Touch