Laravel 12 CRUD API with Error Handling

Author

Kritim Yantra

Mar 23, 2025

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.
Both methods ensure consistent JSON responses and make your API more maintainable and user-friendly.

Happy coding!

LIVE MENTORSHIP ONLY 5 SPOTS

Laravel Mastery
Coaching Class Program

KritiMyantra

Transform from beginner to Laravel expert with our personalized Coaching Class starting June 23, 2025. Limited enrollment ensures focused attention.

Daily Sessions

1-hour personalized coaching

Real Projects

Build portfolio applications

Best Practices

Industry-standard techniques

Career Support

Interview prep & job guidance

Total Investment
$200
Duration
30 hours
1h/day

Enrollment Closes In

Days
Hours
Minutes
Seconds
Spots Available 5 of 10 remaining
Next cohort starts:
June 23, 2025

Join the Program

Complete your application to secure your spot

Application Submitted!

Thank you for your interest in our Laravel mentorship program. We'll contact you within 24 hours with next steps.

What happens next?

  • Confirmation email with program details
  • WhatsApp message from our team
  • Onboarding call to discuss your goals

Tags

Comments

Alamgir Khan

Alamgir Khan

Apr 18, 2025 04:13 PM

Where you have shown the errors message examples of the following code. $request->validate([ 'name' => 'required|string|max:255', 'description' => 'required|string', 'price' => 'required|numeric', ]);
K

Kritim Yantra

Apr 19, 2025 11:38 AM

Hi Alamgir! 👋 Great question. Laravel automatically returns validation error messages in JSON format when the $request->validate() method fails.

Please log in to post a comment:

Sign in with Google

Related Posts