Laravel 12 CRUD API with Error Handling
In this blog, we'll walk through creating a simple CRUD (Create, Read, Update, Delete) API using Laravel 12. We'll also implement proper error handling to ensure that our API is robust and user-friendly. By the end of this tutorial, you'll have a fully functional API with error handling in place.
Step 1: Setting Up Laravel
First, let's create a new Laravel project. Open your terminal and run the following command:
composer create-project laravel/laravel laravel-crud-api
Once the project is created, navigate into the project directory:
cd laravel-crud-api
Step 2: Database Configuration
Next, we need to configure our database. Open the .env file and update the database credentials:
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=laravel_crud_api
DB_USERNAME=root
DB_PASSWORD=
Now, run the migrations to create the necessary tables:
php artisan migrate
Install API:
php artisan install:api
Step 3: Creating the Model and Migration
Let's create a model and migration for our Product entity. Run the following command:
php artisan make:model Product -m
This will create a Product model and a migration file. Open the migration file located in database/migrations and define the schema:
public function up()
{
Schema::create('products', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->text('description');
$table->decimal('price', 8, 2);
$table->timestamps();
});
}
Run the migration to create the products table:
php artisan migrate
Step 4: Creating the Controller
Now, let's create a controller to handle our CRUD operations. Run the following command:
php artisan make:controller ProductController --api
This will generate a controller with the basic CRUD methods. Open the ProductController located in app/Http/Controllers and implement the methods:
namespace App\Http\Controllers;
use App\Models\Product;
use Illuminate\Http\Request;
class ProductController extends Controller
{
// Get all products
public function index()
{
$products = Product::all();
return response()->json($products);
}
// Get a single product
public function show($id)
{
$product = Product::find($id);
if (!$product) {
return response()->json(['message' => 'Product not found'], 404);
}
return response()->json($product);
}
// Create a new product
public function store(Request $request)
{
$request->validate([
'name' => 'required|string|max:255',
'description' => 'required|string',
'price' => 'required|numeric',
]);
$product = Product::create($request->all());
return response()->json($product, 201);
}
// Update a product
public function update(Request $request, $id)
{
$product = Product::find($id);
if (!$product) {
return response()->json(['message' => 'Product not found'], 404);
}
$request->validate([
'name' => 'sometimes|string|max:255',
'description' => 'sometimes|string',
'price' => 'sometimes|numeric',
]);
$product->update($request->all());
return response()->json($product);
}
// Delete a product
public function destroy($id)
{
$product = Product::find($id);
if (!$product) {
return response()->json(['message' => 'Product not found'], 404);
}
$product->delete();
return response()->json(['message' => 'Product deleted']);
}
}
Step 5: Defining Routes
Next, let's define the routes for our API. Open the routes/api.php file and add the following routes:
use App\Http\Controllers\ProductController;
Route::apiResource('products', ProductController::class);
This single line will create all the necessary routes for our CRUD operations.
Step 6: Testing the API
Now that our API is set up, let's test it using Postman or any other API testing tool.
Create a Product (POST /api/products)
Send a POST request to /api/products with the following JSON body:
{
"name": "Laptop",
"description": "A high-end gaming laptop",
"price": 1500.00
}
Get All Products (GET /api/products)
Send a GET request to /api/products to retrieve all products.
Get a Single Product (GET /api/products/{id})
Send a GET request to /api/products/1 to retrieve a single product.
Update a Product (PUT /api/products/{id})
Send a PUT request to /api/products/1 with the following JSON body:
{
"name": "Updated Laptop",
"price": 1600.00
}
Delete a Product (DELETE /api/products/{id})
Send a DELETE request to /api/products/1 to delete the product.
Step 7: Error Handling
Laravel provides a robust error handling mechanism out of the box, but we can customize it to return consistent JSON responses.
7.1 Using a Custom Form Request
The cleanest way is to extract validation logic into a Form Request and override failedValidation() to customize the response:
php artisan make:request StoreProductRequest
In app/Http/Requests/StoreProductRequest.php:
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Contracts\Validation\Validator;
use Illuminate\Http\Exceptions\HttpResponseException;
class StoreProductRequest extends FormRequest
{
public function authorize()
{
return true;
}
public function rules()
{
return [
'name' => 'required|string|max:255',
'description' => 'required|string',
'price' => 'required|numeric',
];
}
protected function failedValidation(Validator $validator)
{
throw new HttpResponseException(response()->json([
'error' => 'Validation Error',
'messages' => $validator->errors(),
], 422));
}
}
Then in your controller modify the store method signature:
use App\Http\Requests\StoreProductRequest;
public function store(StoreProductRequest $request)
{
$product = Product::create($request->validated());
return response()->json($product, 201);
}
7.2 Manual Validation Inside Controller
If you prefer to keep validation in the controller, use the Validator facade to handle errors manually:
use Illuminate\Support\Facades\Validator;
use Illuminate\Http\Exceptions\HttpResponseException;
public function store(Request $request)
{
$validator = Validator::make($request->all(), [
'name' => 'required|string|max:255',
'description' => 'required|string',
'price' => 'required|numeric',
]);
if ($validator->fails()) {
throw new HttpResponseException(response()->json([
'error' => 'Validation Error',
'messages' => $validator->errors(),
], 422));
}
$product = Product::create($validator->validated());
return response()->json($product, 201);
}
Conclusion
In this tutorial, we created a simple Laravel 12 CRUD API and implemented two approaches to error handling:
- Using Form Requests for clean separation and automatic invocation of
failedValidation(). - Manual validation inside controllers with the Validator facade.
Happy coding!
Projects to explore next
Source-code projects from the KritiMyantra catalogue. Check each project's details before buying.
Flask Authentication System (Free Version) - Kritim Yantra
🛠 Features✔ Google & GitHub Authentication✔ User Registration & Login✔ Profile/Dashboard Access✔ Secure & Mi...
Flask Blog CMS with Admin Dashboard — Python Source Code
A Flask-based blogging website with a built-in admin dashboard. Manage articles, images, categories, tags and featured p...
Laravel 12 + ReactJS Starter Kit: Add Multi-Language Support (Arabic, English, Spanish) with Blog CRUD
🚀 Installation & Setup Download & Extract Get the ZIP from kritimyantra.com and extract it. Includes preconfigu...
Comments
Sign in to join the discussion.