Welcome back to the React Mastery Series!
In the previous article, we explored Redux Toolkit and learned how enterprise applications use centralized state management to build scalable and predictable React applications.
Today, we'll cover another critical skill every React developer must master:
API Integration in React
Most React applications don't work in isolation. They communicate with backend services to:
- Authenticate users
- Fetch customer data
- Display products
- Process payments
- Upload files
- Update user profiles
Understanding how to interact with APIs efficiently is essential for building production-ready applications.
What is an API?
An API (Application Programming Interface) acts as a bridge between the frontend and backend.
React Application
|
↓
REST API
|
↓
Database
Instead of directly accessing the database, the React application sends requests to the backend API.
HTTP Request Methods
The most common HTTP methods are:
| Method | Purpose |
|---|---|
| GET | Retrieve data |
| POST | Create new data |
| PUT | Update existing data |
| PATCH | Partially update data |
| DELETE | Remove data |
Example:
GET /users
POST /users
PUT /users/101
DELETE /users/101
Fetch API
The Fetch API is built into modern browsers.
Example:
async function fetchUsers() {
const response = await fetch("/api/users");
const users = await response.json();
console.log(users);
}
Flow:
React
|
fetch()
|
Backend
|
JSON Response
|
React UI
Handling Errors with Fetch
A common mistake is assuming every response is successful.
async function fetchUsers() {
try {
const response = await fetch("/api/users");
if (!response.ok) {
throw new Error("Failed to fetch users");
}
const users = await response.json();
console.log(users);
} catch (error) {
console.error(error);
}
}
Always check response.ok.
Why Many Teams Prefer Axios
Although Fetch is powerful, enterprise projects often use Axios because it provides:
- Automatic JSON transformation
- Request interceptors
- Response interceptors
- Request cancellation
- Timeout configuration
- Better error handling
Installation:
npm install axios
Making a GET Request with Axios
import axios from "axios";
async function fetchUsers() {
const response = await axios.get("/api/users");
console.log(response.data);
}
Notice:
Fetch
↓
response.json()
vs.
Axios
↓
response.data
Axios automatically parses JSON.
Sending a POST Request
Example:
await axios.post("/api/users", {
name: "Siva",
role: "Frontend Developer"
});
Flow:
Form
|
Submit
|
POST Request
|
Backend
|
Database
PUT and DELETE Requests
Update:
await axios.put( "/api/users/101",
{
name: "Updated Name"
}
);
Delete:
await axios.delete("/api/users/101");
Creating a Reusable Axios Instance
Instead of repeating configuration:
axios.get(...)
axios.post(...)
axios.put(...)
Create one API client.
import axios from "axios";
export const api = axios.create({
baseURL: "https://api.example.com",
timeout: 10000
});
Now:
api.get("/users");
api.post("/users");
Configuration is centralized.
Request Interceptors
Most enterprise applications require an authentication token.
Instead of adding it manually:
headers: {
Authorization:"Bearer token"
}
Use an interceptor.
api.interceptors.request.use(config => {
const token = localStorage.getItem("token");
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
});
Every request automatically includes the token.
Response Interceptors
Suppose the backend returns:
401 Unauthorized
Instead of handling it everywhere:
if(error.response.status===401){
// Logout
}
Create a response interceptor.
api.interceptors.response.use(
response => response,
error => {
if(error.response?.status===401){
// Redirect to Login
}
return Promise.reject(error);
});
One place handles authentication failures.
Loading States
Always show feedback while data loads.
const [loading, setLoading] = useState(true);
Example:
if (loading) {
return <Spinner />;
}
Never leave users wondering if the application is working.
Error States
Display meaningful messages.
if (error) {
return (<ErrorMessage message={error.message} />);
}
Avoid showing raw server errors to users.
Empty States
An API can return an empty list.
if(users.length===0){
return <NoUsersFound />;
}
A good UI handles:
- Loading
- Success
- Error
- Empty data
API Service Layer
Avoid calling APIs directly inside components.
❌ Bad:
function Dashboard(){
useEffect(()=>{
axios.get("/users");
},[]);
}
✅ Better:
services
└── userService.ts
export function getUsers(){
return api.get("/users");
}
Component:
const users = await getUsers();
Business logic stays separate from UI.
Custom Hooks for API Calls
Combine services with Custom Hooks.
function useUsers(){
// Fetch users
// Handle loading
// Handle errors
}
Component:
const { users, loading,error }=useUsers();
The component focuses only on rendering.
API Folder Structure
A scalable structure:
src
├── api
│ ├── axios.ts
│ ├── interceptors.ts
│
├── services
│ ├── authService.ts
│ ├── userService.ts
│ ├── accountService.ts
│ └── transactionService.ts
Each service manages one business domain.
Real-World Banking Example
Customer opens the dashboard.
Dashboard
|
useAccounts()
|
accountService
|
Axios Client
|
REST API
|
Customer Accounts
The UI doesn't know how the request is made.
It simply displays the returned data.
Retry Strategy
Network failures happen.
Example:
Request
↓
Network Error
↓
Retry
↓
Success
Libraries like Axios can be combined with retry mechanisms to improve reliability.
Security Best Practices
Never:
- Store sensitive information in source code.
- Expose API keys in frontend applications.
- Trust client-side validation alone.
Always:
- Use HTTPS.
- Store tokens securely.
- Validate input on both frontend and backend.
Common Mistakes
1. Calling APIs Inside Every Component
Duplicate requests waste bandwidth.
Move API logic into services and reusable hooks.
2. Ignoring Errors
Always handle:
- 400 Bad Request
- 401 Unauthorized
- 403 Forbidden
- 404 Not Found
- 500 Internal Server Error
Provide user-friendly feedback.
3. Mixing UI and Business Logic
Keep components focused on rendering.
Move networking code into services.
4. Forgetting Cleanup
When a component unmounts during an API request, avoid updating state afterward.
Use request cancellation or cleanup mechanisms when appropriate.
Best Practices
- Centralize Axios configuration.
- Use request and response interceptors.
- Separate services from UI.
- Display loading, error, and empty states.
- Reuse API logic through Custom Hooks.
- Handle authentication globally.
- Keep API endpoints configurable through environment variables.
Key Takeaways
Today, we learned:
✅ React applications communicate with backend services using APIs.
✅ Fetch and Axios are the two most common approaches.
✅ Axios provides powerful features like interceptors and automatic JSON parsing.
✅ API logic should live in a service layer, not inside components.
✅ Every application should handle loading, error, and empty states gracefully.
✅ Combining services with Custom Hooks leads to clean and maintainable code.
Coming Next 🚀
In Day 24, we will explore:
React Forms – Controlled Components, Validation & React Hook Form
We will learn:
- Controlled vs Uncontrolled Components
- Handling form state
- Input validation
- Form submission
- React Hook Form
- Schema validation with Zod/Yup
- Dynamic forms
- Enterprise form best practices
Forms are one of the most common features in any React application, and mastering them is essential for building production-ready user interfaces.
Happy Coding! 🚀
Top comments (0)