React Organizing API Calls

In the last lesson, React Organizing Hooks, you gave your hooks a tidy home. This lesson does the same for the code that talks to your backend, so you organize API calls into one clean service layer instead of scattering raw fetch everywhere.

🤔 Why organize API calls?

Here’s the pain. Raw fetch calls spread through your components cause real problems.

  • The same base URL is written in many components, so changing it means editing them all.
  • The fetching, JSON parsing, and error handling get copied into every component.
  • When the API changes, you have to hunt down every place that calls it.
  • Components get cluttered with network details instead of focusing on the UI.

So the fix is simple. Put all that network logic in one place, and let components call simple functions.

🗂️ The service layer idea

The core idea is a service layer: a set of files whose only job is talking to the API. Here’s how it works.

  • You make an api/ (or services/) folder for these files.
  • Each file holds the functions for one area, like userApi.js for user calls.
  • A component calls getUsers() instead of writing the raw fetch.

Think of it as a clean boundary. On one side, components that show data. On the other, functions that know how to fetch it.

🧩 A central place for the base URL

Start by putting the base URL and shared fetch logic in one spot, so it’s never repeated.

api/client.js
const BASE_URL = "https://api.example.com";
export async function request(path) {
const res = await fetch(`${BASE_URL}${path}`);
if (!res.ok) {
throw new Error(`Request failed: ${res.status}`);
}
return res.json();
}

Read what this small helper gives you.

  • BASE_URL lives in one place, so you change the server once and every call follows.
  • request handles the common work: building the URL, checking res.ok, and parsing JSON.
  • Every API function can reuse request instead of repeating that logic.

📄 Group calls by area

Next, make a file per area of your API, with one function per call. These use the shared request.

api/userApi.js
import { request } from "./client";
export function getUsers() {
return request("/users");
}
export function getUser(id) {
return request(`/users/${id}`);
}

See how clean each function is.

  • getUsers and getUser each describe one call, with a clear name.
  • They reuse request, so they don’t repeat the URL or error handling.
  • All user-related calls live together in userApi.js, easy to find.

So the component never sees a raw fetch. It just calls getUsers() or getUser(id), which read like plain English.

🖥️ Using the service from a component

Now the component is clean. It imports the function and calls it, with no network details.

import { useState, useEffect } from "react";
import { getUsers } from "../api/userApi";
function UserList() {
const [users, setUsers] = useState([]);
useEffect(() => {
getUsers().then(setUsers).catch(console.error);
}, []);
return (
<ul>
{users.map((u) => (
<li key={u.id}>{u.name}</li>
))}
</ul>
);
}

Notice what the component does and doesn’t do.

  • It calls getUsers(), a clear function, instead of writing fetch with a full URL.
  • It doesn’t know or care about the base URL, the JSON parsing, or the status check.
  • If the API URL changes, this component doesn’t change at all; only client.js does.

So the component focuses on the UI, and all the network logic stays in the service layer. That’s the whole win.

Pairs well with custom hooks

You can go one step further and wrap these service calls in a custom hook, like useUsers(), which calls getUsers() and manages loading and error state. The service layer fetches; the hook adds the React state around it. Clean layers, each with one job.

⚠️ Common Mistakes

The biggest mistake is repeating the full URL and fetch logic in every component.

// ❌ raw fetch with the full URL, repeated in many components
fetch("https://api.example.com/users").then((r) => r.json());
// ✅ call a clean service function; details live in one place
import { getUsers } from "../api/userApi";
getUsers();

Keep these in mind.

  • Don’t repeat the base URL across components. Keep it in one central file.
  • Don’t put raw fetch and error handling in components. Move it to the service layer.
  • Don’t mix unrelated calls in one file; group them by area, like userApi, postApi.

✅ Best Practices

A few habits for organizing API calls.

  • Put all API logic in a dedicated api/ (or services/) folder.
  • Keep the base URL and shared fetch/error logic in one central helper.
  • Group calls by area, one file per area, one clear function per call.
  • Have components call those functions, not raw fetch; optionally wrap them in custom hooks.

Easier to change and test

A service layer makes change easy: swap the base URL, add a header, or change error handling in one place. It also makes testing simpler, because you can test or mock the service functions on their own, away from the UI.

🧩 What You’ve Learned

  • ✅ Scattered raw fetch calls with repeated URLs make an app fragile and hard to change
  • ✅ A service layer puts all API logic in one place, so components call clean functions
  • ✅ Keep the base URL and shared fetch/error logic in one central helper
  • ✅ Group calls by area (one file per area) with one clear function per call
  • ✅ Components call functions like getUsers(), never raw fetch with full URLs
  • ✅ This makes the API easy to change and test, and keeps components focused on the UI

Check Your Knowledge

Test what you learned. Pick an answer for each question, then click Check.

  1. 1

    What is a service layer for API calls?

    Why: The service layer holds all the network logic. Components import simple functions like getUsers() instead of writing raw fetch calls.

  2. 2

    Why keep the base URL in one central place?

    Why: If the base URL is repeated everywhere, changing it is a hunt across many files. In one helper, you change it once and every call follows.

  3. 3

    How should you group API call functions?

    Why: Group related calls by area into their own file, with a clearly named function per call. This keeps related calls together and easy to find.

  4. 4

    What should a component do instead of writing a raw fetch?

    Why: The component calls a service function and stays focused on the UI. The base URL, parsing, and error handling all live in the service layer.

🚀 What’s Next?

Now your API calls are tidy and centralized. That base URL and any secret keys shouldn’t be hardcoded, though. Next you’ll learn to use environment variables to keep configuration out of your code.

React Environment Variables

Share & Connect