
Laravel REST API Part 5: Add Products List API
Published on Dec 3, 2024
After we added our migrations and authentication and also seeded some data into the database. We are going to create products list api. We will try to simulate exactly what we do in a real life scenario, so you will be able to deliver high quality and well performing API's after this short series.
In a real-world development scenario, requirements for a feature are typically written on tools like Jira. Let's imagine a situation where the requirement specifies the creation of an API to display a list of products. The API should allow filtering products by name, price range, stock range, and category ID. When discussing those requirements with the team, it turns out that we will need an api that might look like this:
[GET] /api/products REQUEST: [ product_name, price_from, price_to, stock_from, stock_to, category_id ] RESPONSE: [ { id, name, price, stock, created_at, category: { id, name } } ]
We are now have a clear requirements, let's start building our API
Step 1: Setting Up the Route
We'll start by defining a protected route for fetching the product list. Here's how the route should look:
use App\Http\Controllers\ProductController; Route::middleware('auth:sanctum')->group(function () { Route::get('products', [ProductController::class, 'index'])->name('products'); });
This route is protected by middleware to ensure only authenticated users can access it.
Step 2: Creating the ProductController
Next, create a ProductController with an index method to handle the request:
php artisan make:controller ProductController
In the index method, we'll retrieve and return all products. Initially, it may look like this:
use App\Models\Product; public function index(Request $request) { $products = Product::all(); return response()->json($products); }
Step 3: Using API Resources for Structured Responses
To make the responses organized and readable, we’ll use Laravel API resources. Create resources for Product and Category:
php artisan make:resource ProductResource php artisan make:resource CategoryResource
In ProductResource, define the structure for the product response:
use Illuminate\Http\Resources\Json\JsonResource; class ProductResource extends JsonResource { public function toArray($request) { return [ 'id' => $this->id, 'name' => $this->name, 'price' => $this->price, 'stock' => $this->stock, 'created_at' => $this->created_at, 'category' => new CategoryResource($this->whenLoaded('category')), ]; } }
Note: If you did not provide whenLoaded categories will be lazy loaded and this will cause n+1 problem, meaning that you will have a new query to the database with each product you have trying to get it's category + the original query. This will happen if you did not eager load it in the main query.
In CategoryResource, define the structure for the category response:
use Illuminate\Http\Resources\Json\JsonResource; class CategoryResource extends JsonResource { public function toArray($request) { return [ 'id' => $this->id, 'name' => $this->name, ]; } }
Step 4: Adding Filters to the Query
To allow filtering, we modify the index method in ProductController:
public function index(Request $request) { $query = Product::query(); // Filtering by name if ($request->has('product_name')) { $query->where('name', 'like', '%' . $request->product_name . '%'); } // Filtering by price range if ($request->has('price_from')) { $query->where('price', '>=', $request->price_from); } if ($request->has('price_to')) { $query->where('price', '<=', $request->price_to); } // Filtering by stock range if ($request->has('stock_from')) { $query->where('stock', '>=', $request->stock_from); } if ($request->has('stock_to')) { $query->where('stock', '<=', $request->stock_to); } // Filtering by category ID if ($request->has('category_id')) { $query->where('category_id', $request->category_id); } // Eager loading the category relation $query->with('category'); // Ordering by name $query->orderBy('name'); // Paginating the results $products = $query->paginate(15); return ProductResource::collection($products); }
Step 5: Testing the API
Use Postman or another API testing tool to test the endpoint. Here’s how to set up the request:
- URL: https://localhost/api/products
- Method: GET
- Headers: Include the authorization token (if using Sanctum).
- Query Parameters: Add filters like product_name, price_from, price_to, stock_from, stock_to, and category_id.
- Authorization: Don't forget to add Bearer {token}
Conclusion
Our API is now ready, complete with filters, eager loading, and proper structuring using API resources. In a follow-up article, we’ll explore writing unit tests for this API and refactoring the controller for better readability and reusability.
If you are a visual learner and prefer video tutorials you can check it on our youtube channel
Stay tuned for more Laravel tutorials!