Skip to main content

Stop putting fetch in components

Here’s what most beginners do — scatter fetch() calls throughout their components:
This works, but it creates problems: the same URL and error-handling logic gets duplicated in every component that needs users. If the API changes, you fix it in 10 places instead of 1.
The companion repo already uses an API client file (frontend/src/api/users.ts) with this pattern. In this lesson, we stay in JavaScript and then add an optional shared client.js helper as a teaching step to reduce repetition further.

The API client pattern

Create a dedicated file for each resource’s API calls:
Now your components are clean:
The component doesn’t know about URLs, headers, or HTTP methods. It just calls getUsers() and gets data back.

File structure

One API file per resource. Each file exports functions that match the backend endpoints.
This mirrors how your FastAPI backend is organized. You have a users router in Python and a users.js API client in JavaScript. When you add a new endpoint to FastAPI, you add a matching function to the API client.

Reducing duplication with a helper

Notice how every function repeats the same pattern: fetch, check response, parse JSON. Extract a helper:
Now your API functions are one-liners:
The apiClient helper reads the FastAPI error message (error.detail) when available. This means your backend’s validation errors (“Email is required”) show up directly in the frontend — no extra work needed.

Why this pattern matters

This isn’t over-engineering — it’s the minimum level of organization you need once your app has more than 2-3 API calls.

Using the API client in components

Clean, readable, and every API call is one function call.

What’s next?

Your API calls are organized. But how do you make sure the data your frontend sends matches what your backend expects? Let’s talk about type safety.

Type safety

Ensure your frontend and backend agree on data shapes