Tired of API Madness in React? The Adapter Pattern Keeps Things Simple!
Saurabh Suman
Experienced Full Stack Developer | React.js | Next.js | Node.js | Typescript
This article explores how the Adapter Pattern can simplify your React code, reduce headaches, and make your data layer more maintainable. Let’s dive in!
Recently my organization implemented this design pattern and trust me. it saves so much effort and time from finding and fixing bugs to improve the overall productivity
What's the Problem? The Pain Points of API Integration
You often deal with multiple APIs or data sources when building React apps. Each API might have a different structure or format, forcing you to write custom code in your components to handle these inconsistencies. This leads to:
Repository Pattern: What Is It?
The Repository Pattern abstracts the data layer, centralizing and managing all API interactions within the project. It provides a structured way to access data while keeping the business logic separate from the data source.
Adapter Pattern: What Is It?
The Adapter Pattern serves as a bridge between different interfaces. In React, it transforms raw API data into a format that components can use effectively. Think of it as a translator, ensuring seamless communication between your application and external APIs.
Use Cases for the Repository and Adapter Patterns:
领英推荐
2. Switching Data Sources Easily:
These patterns help solve real-world problems by promoting clean architecture, reusability, and maintainability in complex applications.
let's understand with the help of a project
Organize the project with a clear separation of concern
src/
├── adapters/
│ └── apiAdapter.js
├── components/
│ └── ItemsComponent.js
├── repositories/
│ └── dataRepository.js
├── services/
│ └── apiService.js
├── App.js
Now create repositories dataRepository.js
import apiAdapter from 'src/adaptors/apiAdaptors'
const DataRepository = () => ({
fetchAllProducts: async () => {
return await apiAdapter.get('/products');
},
fetchProductById: async (id) => {
return await apiAdapter.get(`/products/${id}`);
},
addProduct: async (id) => {
return await apiAdapter.post('/products', id);
},
editProduct: async (productId, data) => {
return await apiAdapter.put(`/products/${productId}`, data);
},
removeProduct: async (id) => {
return await apiAdapter.delete(`/products/${id}`);
}
});
export default DataRepository;
src/adaptor/apiAdapter
import axios from 'axios';
const ApiAdapter = () => {
const client = axios.create({
baseURL: process.env.API_URL || 'https://example.com/api'
headers: { 'Content-Type': 'application/json' },
});
return {
get: async (url) => {
const response = await client.get(url);
return response.data;
},
post: async (url, data) => {
const response = await client.post(url, data);
return response.data;
},
put: async (url, data) => {
const response = await client.put(url, data);
return response.data;
},
delete: async (url) => {
const response = await client.delete(url);
return response.data;
}
};
};
export default ApiAdapter;
components/itemComponent
import React, { useEffect, useState } from 'react';
import DataRepository from 'src/repositories/dataRepository';
const ProductList = () => {
const [products, setProducts] = useState([]);
const [error, setError] = useState(null);
useEffect(() => {
const fetchProducts = async () => {
try {
const response = await DataRepository.fetchAllProducts();
setProducts(response.data);
} catch (err) {
setError(err.message || 'Something went wrong');
}
};
fetchProducts();
}, []);
if (error) return <div>Error: {error}</div>;
if (!products.length) return <div>Loading...</div>;
return (
<div>
<h2>Product List</h2>
<ul>
{products.map((product) => (
<li key={product.id}>{product.name}</li>
))}
</ul>
</div>
);
};
export default ProductList;