Laravel 11 REST API CRUD with best practices

Sandalanka
5 min readMar 16, 2024

--

Creating a RESTful API CRUD application in Laravel 11 with best practices involves several steps, including setting up your Laravel application, defining routes, validation, model, resources, creating controllers, implementing the Repository design pattern, and working with models. Here’s a step-by-step guide on how to achieve this:

Step 1: Setting up Laravel

composer create-project --prefer-dist laravel/laravel rest-api-crud

Step 2: Mysql database configuration

Laravel 11 default DB_CONNECTION=sqlite.you have to change this DB_CONNECTION=mysql this is in env file.

Step 03: Create the Product Model with migration

php artisan make:model Product -a

Step 04: Migration

In database/migrations/YYYY_MM_DD_HHMMSS_create_products_table.php, update the up function to match the following.

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

Step 05: create product interface

Create a repository interface for the Product model. This separation allows for cleaner and more maintainable code.

php artisan make:interface /Interfaces/ProductRepositoryInterface

in the Interfaces, create a new file called ProductRepositoryInterface.php and add the following code to it.

<?php

namespace App\Interfaces;

interface ProductRepositoryInterface
{
public function index();
public function getById($id);
public function store(array $data);
public function update(array $data,$id);
public function delete($id);
}

Step 06: create product respository class

Create a repository class for the Product model.

php artisan make:class /Repositories/ProductRepository

in the classes, create a new file called ProductRepository.php and add the following code to it.

<?php

namespace App\Repository;
use App\Models\Product;
use App\Interfaces\ProductRepositoryInterface;
class ProductReposiotry implements ProductRepositoryInterface
{
public function index(){
return Product::all();
}

public function getById($id){
return Product::findOrFail($id);
}

public function store(array $data){
return Product::create($data);
}

public function update(array $data,$id){
return Product::whereId($id)->update($data);
}

public function delete($id){
Product::destroy($id);
}
}

Step 07: Bind the interface and the implementation

we need to do is bind ProductRepository to ProductRepositoryInterface.we do this via a Service Provider. Create one using the following command.

php artisan make:provider RepositoryServiceProvider

Open app/Providers/RepositoryServiceProvider.php and update the register function to match the following

<?php

namespace App\Providers;

use Illuminate\Support\ServiceProvider;
use App\Interfaces\ProductRepositoryInterface;
use App\Repository\ProductReposiotry;
class RepositoryServiceProvider extends ServiceProvider
{
/**
* Register services.
*/
public function register(): void
{
$this->app->bind(ProductRepositoryInterface::class,ProductReposiotry::class);
}

/**
* Bootstrap services.
*/
public function boot(): void
{
//
}
}

Step 08: Request validation

we have two request StoreProductRequest and UpdateProductRequest add the following code to it.

<?php

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Http\Exceptions\HttpResponseException;
use Illuminate\Contracts\Validation\Validator;
class StoreProductRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*/
public function authorize(): bool
{
return true;
}

/**
* Get the validation rules that apply to the request.
*
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
*/
public function rules(): array
{
return [
'name' => 'required',
'details' => 'required'
];
}

public function failedValidation(Validator $validator)
{
throw new HttpResponseException(response()->json([
'success' => false,
'message' => 'Validation errors',
'data' => $validator->errors()
]));
}
}
<?php

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Http\Exceptions\HttpResponseException;
use Illuminate\Contracts\Validation\Validator;
class UpdateProductRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*/
public function authorize(): bool
{
return true;
}

/**
* Get the validation rules that apply to the request.
*
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
*/
public function rules(): array
{
return [
'name' => 'required',
'details' => 'required'
];
}

public function failedValidation(Validator $validator)
{
throw new HttpResponseException(response()->json([
'success' => false,
'message' => 'Validation errors',
'data' => $validator->errors()
]));
}
}

Step 09: Common ApiResponseClass create

This common response class is the best practice thing. Because you can response send con function use. Create one using the following command

php artisan make:class /Classes/ApiResponseClass

Add the following code to it.

<?php

namespace App\Classes;
use Illuminate\Support\Facades\DB;
use Illuminate\Http\Exceptions\HttpResponseException;
use Illuminate\Support\Facades\Log;
class ApiResponseClass
{
public static function rollback($e, $message ="Something went wrong! Process not completed"){
DB::rollBack();
self::throw($e, $message);
}

public static function throw($e, $message ="Something went wrong! Process not completed"){
Log::info($e);
throw new HttpResponseException(response()->json(["message"=> $message], 500));
}

public static function sendResponse($result , $message ,$code=200){
$response=[
'success' => true,
'data' => $result
];
if(!empty($message)){
$response['message'] =$message;
}
return response()->json($response, $code);
}

}

Step 10: create product resource

Create one using the following command.

php artisan make:resource ProductResource

Add the following code to it.

<?php

namespace App\Http\Resources;

use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;

class ProductResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* @return array<string, mixed>
*/
public function toArray(Request $request): array
{
return [
'id' =>$this->id,
'name' => $this->name,
'details' => $this->details
];
}
}

Step 11: productcontroller class

With our repository in place, let’s add some code to our controller. Open app/Http/Controllers/ProductController.php and update the code to match the following.

<?php

namespace App\Http\Controllers;

use App\Models\Product;
use App\Http\Requests\StoreProductRequest;
use App\Http\Requests\UpdateProductRequest;
use App\Interfaces\ProductRepositoryInterface;
use App\Classes\ResponseClass;
use App\Http\Resources\ProductResource;
use Illuminate\Support\Facades\DB;
class ProductController extends Controller
{

private ProductRepositoryInterface $productRepositoryInterface;

public function __construct(ProductRepositoryInterface $productRepositoryInterface)
{
$this->productRepositoryInterface = $productRepositoryInterface;
}
/**
* Display a listing of the resource.
*/
public function index()
{
$data = $this->productRepositoryInterface->index();

return ResponseClass::sendResponse(ProductResource::collection($data),'',200);
}

/**
* Show the form for creating a new resource.
*/
public function create()
{
//
}

/**
* Store a newly created resource in storage.
*/
public function store(StoreProductRequest $request)
{
$details =[
'name' => $request->name,
'details' => $request->details
];
DB::beginTransaction();
try{
$product = $this->productRepositoryInterface->store($details);

DB::commit();
return ResponseClass::sendResponse(new ProductResource($product),'Product Create Successful',201);

}catch(\Exception $ex){
return ResponseClass::rollback($ex);
}
}

/**
* Display the specified resource.
*/
public function show($id)
{
$product = $this->productRepositoryInterface->getById($id);

return ResponseClass::sendResponse(new ProductResource($product),'',200);
}

/**
* Show the form for editing the specified resource.
*/
public function edit(Product $product)
{
//
}

/**
* Update the specified resource in storage.
*/
public function update(UpdateProductRequest $request, $id)
{
$updateDetails =[
'name' => $request->name,
'details' => $request->details
];
DB::beginTransaction();
try{
$product = $this->productRepositoryInterface->update($updateDetails,$id);

DB::commit();
return ResponseClass::sendResponse('Product Update Successful','',201);

}catch(\Exception $ex){
return ResponseClass::rollback($ex);
}
}

/**
* Remove the specified resource from storage.
*/
public function destroy($id)
{
$this->productRepositoryInterface->delete($id);

return ResponseClass::sendResponse('Product Delete Successful','',204);
}
}

Step 12:Api route

Executing the subsequent command allows you to publish the API route file:

php artisan install:api

To map each method defined in the controller to specific routes, add the following code to routes/api.php.

<?php

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\ProductController;
Route::get('/user', function (Request $request) {
return $request->user();
})->middleware('auth:sanctum');


Route::apiResource('/products',ProductController::class);

Now you can run the project.

Thanks…………

--

--