Stop 19 · Frontend System DesignTopics · 8/13

API Layer

Part of the Frontend Developer Roadmap

In short

When fetch calls are scattered across 40 components, changing the base URL or adding an auth header becomes a weekend job. An API layer puts all of that in one place.

A dedicated, centralized module for all API calls — instead of scattering `fetch`/`axios` calls throughout components — making endpoints easy to find, test and change.

The jaadu receptionist talks on a phone at a hotel desk, pointing a guest to the right door.

Think of it like this

The hotel reception desk

Guests don't walk into the kitchen or laundry room themselves. They call reception, and reception knows who to ask and how. Your components call the API layer, and it handles URLs, headers and errors.

Watch it happen

One call through the API layer

Component
lib/api.ts
Backend

getCourses()

1/6The component calls a named function. It never sees a URL.

The big ideas

What you need to know

One client for every call

A single wrapper adds the base URL, auth token and JSON handling to every request.

Typed functions per endpoint

Components call getCourses() instead of writing URLs, and get typed data back.

Errors handled the same way

The client turns failed responses into errors in one shape, so the UI handles them consistently.

Easy to swap or mock

Tests and new backends only need changes in the API layer, not in components.

See it in code

Walk through the code

ts
1// lib/api.ts2const BASE = process.env.NEXT_PUBLIC_API_URL;3 4async function request<T>(path: string, init?: RequestInit): Promise<T> {5  const res = await fetch(`${BASE}${path}`, {6    ...init,7    headers: { "Content-Type": "application/json", ...init?.headers },8  });9  if (!res.ok) throw new Error(`API ${res.status}: ${path}`);10  return res.json() as Promise<T>;11}12 13export const getCourses = () => request<Course[]>("/courses");14export const enroll = (id: string) =>15  request<void>(`/courses/${id}/enroll`, { method: "POST" });

1/4The base URL is set once, in one place.

fetch doesn't throw on 404 or 500, so the res.ok check matters. Every call now fails the same way, and components never see raw URLs.

Save yourself the pain

Common mistakes

Oops

Calling fetch("https://api.example.com/...") directly inside components.

Do this instead

Wrap every endpoint in a named function in the API layer, and import that function instead.

Oops

Assuming fetch throws when the server returns 500.

Do this instead

fetch only throws on network failures. Check res.ok yourself and throw a clear error.

Your mission

Build your API layer

~30 min

Quick check

Your backend moves from /api/v1 to /api/v2. With a proper API layer, what do you change?

Go deeper

  1. 1Extract scattered `fetch` calls in a project into a single `api/` module.
  2. 2Add a consistent error-handling and auth-header pattern in that one place.
  3. 3Notice how much easier it becomes to change a base URL or add caching afterward.

More topics at this stop

Stuck on this topic? Ask an Insider

Get 1:1 guidance from people who've walked this exact path — on the InsideEdge app.

Download
InsideEdge