Laravel 12 JWT Products & Categories API – Step by Step Guide

Laravel 12 JWT Products & Categories API – Step by Step Guide

In this tutorial, we’ll build a Products & Categories API in Laravel 12 using JWT authentication. We’ll follow Laravel’s RESTful principles with clean controllers, routes, and proper model relationships.

📌 API Endpoints Overview

Categories

MethodEndpointPurpose
GET/api/categoriesList all categories
POST/api/categoriesCreate a category
GET/api/categories/{id}Show category details
PUT/api/categories/{id}Update category
DELETE/api/categories/{id}Delete category

Products

MethodEndpointPurpose
GET/api/productsList all products
POST/api/productsCreate a product
GET/api/products/{id}Show product details
PUT/api/products/{id}Update product
DELETE/api/products/{id}Delete product

Step 1: Create Models & Migrations

php artisan make:model Category -m php artisan make:model Product -m

Migration: database/migrations/xxxx_xx_xx_create_categories_table.php

public function up() { Schema::create('categories', function (Blueprint $table) { $table->id(); $table->string('name')->unique(); $table->timestamps(); }); }

Migration: database/migrations/xxxx_xx_xx_create_products_table.php

public function up() { Schema::create('products', function (Blueprint $table) { $table->id(); $table->string('name'); $table->text('description')->nullable(); $table->decimal('price', 10, 2); $table->unsignedBigInteger('category_id'); $table->foreign('category_id')->references('id')->on('categories')->onDelete('cascade'); $table->timestamps(); }); }

Run migrations:

php artisan migrate

Step 2: Define Relationships

app/Models/Category.php

class Category extends Model { protected $fillable = ['name']; public function products() { return $this->hasMany(Product::class); } }

app/Models/Product.php

class Product extends Model { protected $fillable = ['name', 'description', 'price', 'category_id']; public function category() { return $this->belongsTo(Category::class); } }

Step 3: Create Controllers

php artisan make:controller Api/CategoryController --api php artisan make:controller Api/ProductController --api

Step 4: CategoryController

app/Http/Controllers/Api/CategoryController.php

namespace App\Http\Controllers\Api; use App\Http\Controllers\Controller; use App\Models\Category; use Illuminate\Http\Request; class CategoryController extends Controller { public function index() { return response()->json(Category::all(), 200); } public function store(Request $request) { $validated = $request->validate([ 'name' => 'required|string|unique:categories,name', ]); $category = Category::create($validated); return response()->json($category, 201); } public function show($id) { return response()->json(Category::findOrFail($id), 200); } public function update(Request $request, $id) { $category = Category::findOrFail($id); $validated = $request->validate([ 'name' => 'required|string|unique:categories,name,' . $category->id, ]); $category->update($validated); return response()->json($category, 200); } public function destroy($id) { Category::findOrFail($id)->delete(); return response()->json(['message' => 'Category deleted successfully'], 200); } }

Step 5: ProductController

app/Http/Controllers/Api/ProductController.php

namespace App\Http\Controllers\Api; use App\Http\Controllers\Controller; use App\Models\Product; use Illuminate\Http\Request; class ProductController extends Controller { public function index() { return response()->json(Product::with('category')->get(), 200); } public function store(Request $request) { $validated = $request->validate([ 'name' => 'required|string', 'description' => 'nullable|string', 'price' => 'required|numeric|min:0', 'category_id' => 'required|exists:categories,id', ]); $product = Product::create($validated); return response()->json($product, 201); } public function show($id) { return response()->json(Product::with('category')->findOrFail($id), 200); } public function update(Request $request, $id) { $product = Product::findOrFail($id); $validated = $request->validate([ 'name' => 'required|string', 'description' => 'nullable|string', 'price' => 'required|numeric|min:0', 'category_id' => 'required|exists:categories,id', ]); $product->update($validated); return response()->json($product, 200); } public function destroy($id) { Product::findOrFail($id)->delete(); return response()->json(['message' => 'Product deleted successfully'], 200); } }

Step 6: Define Routes

routes/api.php

use App\Http\Controllers\Api\CategoryController; use App\Http\Controllers\Api\ProductController; Route::middleware('auth:api')->group(function () { Route::apiResource('categories', CategoryController::class)->except(['create', 'edit']); Route::apiResource('products', ProductController::class)->except(['create', 'edit']); });

Step 7: Testing with Postman

Categories

  • GET /api/categories → List categories

  • POST /api/categories → Create category

{ "name": "Electronics" }
  • GET /api/categories/1 → Show category

  • PUT /api/categories/1 → Update category

{ "name": "Smartphones" }
  • DELETE /api/categories/1 → Delete category

Products

  • GET /api/products → List products with category details

  • POST /api/products → Create product

{ "name": "iPhone 17", "description": "Latest Apple smartphone", "price": 999.99, "category_id": 1 }
  • GET /api/products/1 → Show product

  • PUT /api/products/1 → Update product

{ "name": "iPhone 17 Pro", "description": "Updated model", "price": 1199.99, "category_id": 1 }
  • DELETE /api/products/1 → Delete product

🎯 Conclusion

You now have a fully functional Products & Categories API in Laravel 12 secured with JWT authentication.

This setup includes:

  • ✅ Category management (CRUD)

  • ✅ Product management (CRUD with category relation)

  • ✅ Clean RESTful controllers & routes

  • ✅ Postman-tested endpoints

With this foundation, you can further extend the API by adding authentication, user roles, and permissions as needed. 🚀

Want the full source code?

Download the complete Laravel 12 JWT API Authentication example on my GitHub repo here.

Happy Coding!

Souy Soeng

Souy Soeng

Hi there 👋, I’m Soeng Souy (StarCode Kh)
-------------------------------------------
🌱 I’m currently creating a sample Laravel and React Vue Livewire
👯 I’m looking to collaborate on open-source PHP & JavaScript projects
💬 Ask me about Laravel, MySQL, or Flutter
⚡ Fun fact: I love turning ☕️ into code!

Post a Comment

CAN FEEDBACK
close