Networking: Fetch & Axios
Almost every real app needs to talk to a server — loading a list of articles, submitting a
form, checking whether a login is valid. React Native has no separate "mobile networking API" to
learn: it uses the same fetch() function web developers already know, and the
broader JavaScript ecosystem's HTTP libraries, like axios, work here too. This
lesson covers both, and the differences between them that genuinely matter in practice.
The Built-In fetch() API
fetch() is available globally in React Native — no import needed,
just like in a browser. It returns a Promise that resolves with a
Response object once the server has responded:
async function loadArticles() {
const response = await fetch('https://api.example.com/articles');
const data = await response.json();
return data;
}
Two separate await steps are worth noticing here. The first await
waits for the HTTP response headers to arrive. The second, response.json(),
also returns a Promise — it asynchronously reads and parses the response body as
JSON. Forgetting the second await is a common early mistake that leaves you holding
an unresolved Promise instead of your actual data.
Checking response.ok and response.status
Here's the detail that catches almost everyone coming from other networking libraries:
fetch()'s Promise only rejects on a network-level failure — no
internet connection, a DNS failure, a timeout. A server responding with
404 Not Found or 500 Internal Server Error is still a
"successful" fetch as far as the Promise is concerned; it resolved, it just resolved with an
error status. You have to check this yourself:
async function loadArticles() {
const response = await fetch('https://api.example.com/articles');
if (!response.ok) {
// response.ok is true only for status codes 200-299
throw new Error(`Request failed with status ${response.status}`);
}
const data = await response.json();
return data;
}
response.status gives you the exact numeric HTTP status code (200, 404, 500, and so on), while response.ok is a convenient boolean shortcut for "was it in the 200-299 range." Checking response.ok (or the status code) is not optional — skip it and a 404 page's body might silently get parsed as if it were valid data.Axios: A Popular Alternative
axios is a third-party package (installed via npm/yarn) that wraps HTTP requests
with a friendlier API and a few genuinely useful behaviors fetch() doesn't have
built in:
| Behavior | fetch() | axios |
|---|---|---|
| Parses JSON automatically | No — you must call response.json() yourself | Yes — the parsed body is already on response.data |
| Rejects on error HTTP status (404/500) | No — only rejects on network failure | Yes — throws automatically for non-2xx responses |
| Request/response interceptors | Not built in | Yes — e.g. attach an auth header to every outgoing request in one place |
| Available without installing anything | Yes — global in React Native | No — separate package to install |
try/catch around an axios call already handles both network failures and server error statuses. With fetch(), a try/catch alone only catches network failures — you must manually check response.ok and throw yourself, as shown above, or a 404 will silently flow through as "success."Here's the same GET request from before, written with axios:
import axios from 'axios';
async function loadArticles() {
const response = await axios.get('https://api.example.com/articles');
// no response.ok check, no response.json() call needed
return response.data;
}
Making a POST Request
Sending data with fetch() requires setting the method, headers, and manually
stringifying the request body:
async function createArticle(title, body) {
const response = await fetch('https://api.example.com/articles', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ title, body }),
});
if (!response.ok) {
throw new Error('Failed to create article');
}
return response.json();
}
Axios reduces this to a single call — it sets the JSON content-type header and serializes the body for you automatically:
async function createArticle(title, body) {
const response = await axios.post('https://api.example.com/articles', { title, body });
return response.data;
}
A Reusable Axios Instance: Base URL and Default Headers
In a real app, you're rarely calling one hardcoded URL — you're calling the same API base over
and over, often with a shared header like an API key. axios.create() lets you
configure all of that once, and reuse the resulting instance everywhere instead of repeating
yourself on every call:
// api.js
import axios from 'axios';
const api = axios.create({
baseURL: 'https://api.example.com',
headers: {
'X-Api-Key': 'your-api-key-here',
},
});
export default api;
Every call made through api now automatically uses that base URL and header,
keeping the calling code short and consistent:
import api from './api';
// resolves to https://api.example.com/articles
const response = await api.get('/articles');
Authorization header on every outgoing request, in one central place, instead of repeating that logic at every call site.Choosing Between fetch() and axios
Neither is objectively "better" — fetch() needs no install and is fine for simple
apps with a handful of requests; axios's automatic JSON parsing, automatic error
throwing on bad statuses, and interceptors tend to pay off once an app makes many requests across
many screens. Many production React Native apps use axios specifically to avoid re-implementing
the response.ok check on every single call.
async/await and a bit of error handling. The next lesson goes deeper: Async/Await & Error Handling, including running multiple requests concurrently and a proper loading/error/data UI pattern.