Careeroza
Fetching Products from React
An API Helper
Keep the API base URL in an environment variable (VITE_API_URL) and put all calls in one file:
// src/api/products.js
const API_URL = import.meta.env.VITE_API_URL || 'http://localhost:8080';
async function request(path, options = {}) {
const res = await fetch(API_URL + path, {
headers: { 'Content-Type': 'application/json' },
...options,
});
if (res.status === 204) return null;
const body = await res.json();
if (!res.ok) throw body; // ProblemDetail from Spring: { title, detail, errors }
return body;
}
export const getProducts = () => request('/api/products');
export const createProduct = (product) =>
request('/api/products', { method: 'POST', body: JSON.stringify(product) });
export const deleteProduct = (id) => request('/api/products/' + id, { method: 'DELETE' });The Product List Page
// src/pages/ProductList.jsx
import { useEffect, useState } from 'react';
import { getProducts, deleteProduct } from '../api/products';
export default function ProductList() {
const [products, setProducts] = useState([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
getProducts()
.then(setProducts)
.catch((e) => setError(e.detail || 'Could not load products'))
.finally(() => setLoading(false));
}, []);
async function handleDelete(id) {
await deleteProduct(id);
setProducts((list) => list.filter((p) => p.id !== id));
}
if (loading) return <p>Loading...</p>;
if (error) return <p className="error">{error}</p>;
return (
<table>
<thead>
<tr><th>Name</th><th>Category</th><th>Price</th><th>Stock</th><th></th></tr>
</thead>
<tbody>
{products.map((p) => (
<tr key={p.id}>
<td>{p.name}</td>
<td>{p.category}</td>
<td>₹{p.price.toLocaleString('en-IN')}</td>
<td>{p.inStock ? p.stock : 'Out of stock'}</td>
<td><button onClick={() => handleDelete(p.id)}>Delete</button></td>
</tr>
))}
</tbody>
</table>
);
}What the page renders with the two ShopEase products:
Name Category Price Stock Wireless Mouse Electronics ₹799 120 [Delete] Clean Code Books ₹450 Out of stock [Delete]
Tip: Libraries such as TanStack Query (React Query) add caching, loading states and refetching for you. axios is a popular alternative to fetch; the Spring side is the same.
Dev Proxy Instead of CORS
In development you can also avoid CORS entirely: let Vite forward /api calls to Spring, so the browser sees a single origin.
// vite.config.js
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
export default defineConfig({
plugins: [react()],
server: {
proxy: { '/api': 'http://localhost:8080' },
},
});