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;
}
Note: 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:

Behaviorfetch()axios
Parses JSON automaticallyNo — you must call response.json() yourselfYes — the parsed body is already on response.data
Rejects on error HTTP status (404/500)No — only rejects on network failureYes — throws automatically for non-2xx responses
Request/response interceptorsNot built inYes — e.g. attach an auth header to every outgoing request in one place
Available without installing anythingYes — global in React NativeNo — separate package to install
Important gotcha: Because axios throws automatically on a non-2xx response, a plain 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');
Tip: This same instance is also where request interceptors typically live — for example, automatically attaching a signed-in user's auth token to the 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.

Next up: Every example on this page used 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.