Hello! Let's continue building our full-stack e-commerce application.
In the last lesson, we successfully configured CORS, opening a secure communication channel between your React frontend and your Laravel backend. Now, it's time to put that connection to work.
This lesson focuses on a major milestone: replacing the static, hard-coded product data in your React application with live data fetched directly from your Laravel API. This will make your product catalog dynamic and database-driven, a core feature of any real-world e-commerce site.
We will tackle this in two main parts:
- On the backend: You'll create a simple API endpoint in your Laravel application that serves your product data.
- On the frontend: You'll refactor your React product catalog component to fetch data from that new endpoint and display it.
1. Creating the Product API Endpoint in Laravel
Your first task is to expose your products via an API route. As a Laravel developer, you're already familiar with controllers, routes, and Eloquent models. We will create a route, GET /api/products, that returns a JSON array of all products from your database.
Step 1: Create the Controller
If you don't already have one, create a ProductController using Artisan:
php artisan make:controller Api/ProductController
Placing it in an Api subdirectory is a common convention for API controllers.
Step 2: Add the index Method
Now, open app/Http/Controllers/Api/ProductController.php and add an index method. This method will use your Product Eloquent model to fetch all products and return them as a JSON response.
// app/Http/Controllers/Api/ProductController.php
namespace App\Http\Controllers\Api;
use App\Http\Controllers\Controller;
use App\Models\Product; // Make sure to import your Product model
use Illuminate\Http\Request;
class ProductController extends Controller
{
/**
* Display a listing of the resource.
*/
public function index()
{
// Fetch all products from the database
$products = Product::all();
// Return the products as a JSON response
return response()->json($products);
}
// ... other methods like show, store, etc. can be added later
}
For now, Product::all() is sufficient. Later, you could implement pagination or use Laravel API Resources for more complex data transformations.
Step 3: Define the API Route
Finally, open your routes/api.php file and add a route that maps a GET request to /products to the index method of your new controller.
// routes/api.php
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\Api\ProductController; // Import the controller
/* ... other routes ... */
Route::get('/products', [ProductController::class, 'index']);
With these changes, if you start your Laravel server (php artisan serve) and navigate to http://localhost:8000/api/products in your browser, you should see a JSON array of your products.
2. Fetching Live Data in React
Now for the exciting part: making your React frontend consume this live data. We will modify the component responsible for showing the product list (likely named ProductListingPage, ProductCatalog, or similar).
The following guide provides a fantastic, self-contained example of creating a Laravel API endpoint and a React component to fetch from it. We've already done the backend part, so we'll focus on the React implementation.
How to use Laravel with React & TailwindCSS: Step-by-step guide
The article 'How to use Laravel with React' provides a clear, step-by-step example of connecting a React frontend to a Laravel API. We will focus on the React component it builds to fetch and display data.
Please read the section titled 'Laravel API + React interaction'. Pay close attention to the React component code in '2. Create a React component to fetch the posts' (resources/js/components/PostsList.jsx). Notice how it uses useState, useEffect, and fetch with async/await to manage the entire data-fetching lifecycle.
Let's break down how to apply the pattern from that article to your e-commerce app.
Step 1: Setting up State
In your product catalog component, you'll need state variables to hold the products, track the loading status, and store any potential errors. Remove any static product arrays and replace them with this:
import React, { useState, useEffect } from 'react';
function ProductCatalog() {
const [products, setProducts] = useState([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
// ... fetching logic will go here
}
Step 2: Fetching Data with useEffect
We use the useEffect hook to perform the data fetch as a side effect when the component first renders. The logic will live inside an async function.
It's best practice to define the API's base URL in an environment variable. In your project's root, create or open the .env.local file and add:VITE_API_URL=http://localhost:8000
Now, you can use import.meta.env.VITE_API_URL in your code. This makes it easy to change the API endpoint for different environments (like production) without changing your code.
// Inside your ProductCatalog component
useEffect(() => {
// Define the async function to fetch data
const fetchProducts = async () => {
try {
// Construct the full API URL
const apiUrl = `${import.meta.env.VITE_API_URL}/api/products`;
const response = await fetch(apiUrl, {
// We include credentials for future authenticated requests
credentials: 'include',
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
setProducts(data); // Update state with fetched products
} catch (e) {
setError(e.message); // Store any errors
console.error("Failed to fetch products:", e);
} finally {
setLoading(false); // Set loading to false once done
}
};
fetchProducts();
}, []); // The empty dependency array means this runs once on mount
Step 3: Conditional Rendering
Your component should now handle the different states: loading, error, and success. Before you map over the products, add checks for loading and error states.
// Inside the return statement of your ProductCatalog component
if (loading) {
return <div>Loading products...</div>;
}
if (error) {
return <div>Error: {error}</div>;
}
return (
<div className="product-grid">
{products.map(product => (
// Your existing ProductCard component likely goes here
<ProductCard key={product.id} product={product} />
))}
</div>
);
Your existing rendering logic that maps over the product array remains largely the same; it just uses the products state variable now instead of a static array.
3. Testing and Troubleshooting
With both your Laravel and React development servers running, your product catalog should now display data pulled directly from your database.
If you encounter issues, your browser's Developer Tools are your best friend:
- Check the Console: Look for any CORS errors or JavaScript errors. A CORS error here would mean something in the last lesson's configuration isn't quite right.
- Check the Network Tab: Filter by "Fetch/XHR". Find the request to
/api/products.- Is the request URL correct (
http://localhost:8000/api/products)? - What is the status code? (e.g.,
200 OK,404 Not Found,500 Server Error). - Click on the request and check the "Response" tab to see the exact JSON your Laravel app sent back.
- Is the request URL correct (
The "Common problems & fixes" section in the How to use Laravel with React article you read also offers excellent troubleshooting advice.
Conclusion
Congratulations! You have successfully refactored a key part of your application to be truly full-stack. By creating a Laravel API endpoint and consuming it in React, you've replaced static content with dynamic, live data.
Key Takeaways:
- Laravel API Endpoints: You can easily create API endpoints in
routes/api.phpthat use controllers and Eloquent to serve data from your database as JSON. - React Data Fetching Pattern: The
useEffecthook is the standard place to trigger data fetching when a component mounts. - State Management for Fetching: Using
useStateto managedata,loading, anderrorstates allows you to build a robust and user-friendly UI that responds to the network request's lifecycle. - Environment Variables: Using
VITE_API_URLkeeps your code clean and makes it easy to point your frontend to different backend environments.
Preview of the Next Lesson:
Now that you've mastered fetching public data, the next step is to handle user-specific actions. In the upcoming lesson, we will integrate the React login and registration forms with your Laravel authentication endpoints, leveraging the Sanctum configuration you've already set up to manage user sessions.