Header Ads

Header ADS

Laravel Smart code with Api

 Laravel provides a lot of features out of the box for building APIs. To structure API-related code in a clean and efficient way, you can use API resource controllers, form requests for validation, API resources for transformation, and routes designed specifically for APIs. Here's how you can implement a smart and optimized API structure in Laravel.


1. API Routes

When building an API, you generally want to isolate your routes in the routes/api.php file, which Laravel automatically registers with the /api prefix.


// routes/api.php


use App\Http\Controllers\Api\ProductController;


Route::middleware('auth:sanctum')->group(function () {

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

});


  • Route::apiResource() is a shortcut to automatically create the typical CRUD routes (index, store, show, update, and destroy).
  • You can also apply middleware like auth:sanctum to protect your routes for authenticated users. 

  • 2. API Resource Controller

    In API development, Laravel’s apiResource method helps streamline CRUD operations.


  • // app/Http/Controllers/Api/ProductController.php


    namespace App\Http\Controllers\Api;


    use App\Http\Controllers\Controller;

    use App\Http\Requests\StoreProductRequest;

    use App\Http\Resources\ProductResource;

    use App\Models\Product;

    use Illuminate\Http\Request;


    class ProductController extends Controller

    {

        public function index()

        {

            $products = Product::paginate(10);

            return ProductResource::collection($products);

        }


        public function store(StoreProductRequest $request)

        {

            $product = Product::create($request->validated());

            return new ProductResource($product);

        }


        public function show(Product $product)

        {

            return new ProductResource($product);

        }


        public function update(StoreProductRequest $request, Product $product)

        {

            $product->update($request->validated());

            return new ProductResource($product);

        }


        public function destroy(Product $product)

        {

            $product->delete();

            return response()->json(null, 204);

        }

    }


  • ProductResource::collection() is used for paginated lists or collections of products.
  • new ProductResource($product) is used for individual products.
  • Validation is performed using form request classes (like StoreProductRequest).

  • 3. Form Request for Validation

    You should use form request classes to handle validation and authorization logic, keeping your controller clean.


  • // app/Http/Requests/StoreProductRequest.php


    namespace App\Http\Requests;


    use Illuminate\Foundation\Http\FormRequest;


    class StoreProductRequest extends FormRequest

    {

        public function authorize()

        {

            // Authorization logic (can be customized)

            return true;

        }


        public function rules()

        {

            return [

                'name' => 'required|string|max:255',

                'description' => 'nullable|string',

                'price' => 'required|numeric|min:0',

                'stock' => 'required|integer|min:0',

            ];

        }

    }



  • Benefits:

    • Clean validation separated from the controller.
    • Custom authorization logic can be added to the authorize() method.

    4. API Resources for Response Transformation

    API Resources in Laravel transform your model data to a structured JSON format, making it easy to modify the API output without changing the underlying model.


  • // app/Http/Resources/ProductResource.php


    namespace App\Http\Resources;


    use Illuminate\Http\Resources\Json\JsonResource;


    class ProductResource extends JsonResource

    {

        public function toArray($request)

        {

            return [

                'id' => $this->id,

                'name' => $this->name,

                'description' => $this->description,

                'price' => $this->formatted_price,

                'stock' => $this->stock,

                'category' => new CategoryResource($this->whenLoaded('category')),

            ];

        }

    }



  • Benefits:

    • toArray() method provides flexibility for formatting responses.
    • $this->whenLoaded() ensures relationships (e.g., category) are included only if loaded, which prevents unnecessary database queries.

    5. API Response Formatting

    To follow REST standards and create consistent API responses, you can encapsulate your success and error responses in a helper trait or service.


  • // app/Http/Traits/ApiResponseTrait.php


    namespace App\Http\Traits;


    trait ApiResponseTrait

    {

        protected function successResponse($data, $message = null, $code = 200)

        {

            return response()->json([

                'status' => 'success',

                'message' => $message,

                'data' => $data

            ], $code);

        }


        protected function errorResponse($message = null, $code = 400)

        {

            return response()->json([

                'status' => 'error',

                'message' => $message,

            ], $code);

        }

    }



  • Usage in Controllers:

  • // app/Http/Controllers/Api/ProductController.php

    use App\Http\Traits\ApiResponseTrait;

    class ProductController extends Controller
    {
        use ApiResponseTrait;

        public function show(Product $product)
        {
            return $this->successResponse(new ProductResource($product), 'Product found');
        }

        public function store(StoreProductRequest $request)
        {
            $product = Product::create($request->validated());
            return $this->successResponse(new ProductResource($product), 'Product created', 201);
        }

        public function destroy(Product $product)
        {
            $product->delete();
            return $this->successResponse(null, 'Product deleted', 204);
        }
    }


  • Benefits:

    • Encapsulating responses ensures consistent API response format.
    • Provides flexibility for custom success/error messages.

    6. Pagination for API

    When returning a large dataset, it's good to paginate the response. Laravel makes it easy with the paginate() method:


  • // app/Http/Controllers/Api/ProductController.php


    public function index()

    {

        $products = Product::paginate(15);

        return ProductResource::collection($products);

    }



  • Response Output: Pagination will automatically append meta information like current_page, total, last_page, per_page to the JSON response.

  • 7. Handling Filters and Sorting

    You can implement query parameters like filtering, sorting, and searching in a clean way using scopes in models and query logic in the controller.


  • // app/Models/Product.php


    public function scopeFilter($query, $filters)

    {

        if ($filters['price'] ?? null) {

            $query->where('price', $filters['price']);

        }


        if ($filters['category'] ?? null) {

            $query->whereHas('category', function ($q) use ($filters) {

                $q->where('name', $filters['category']);

            });

        }

    }



  • In the controller:

  • // app/Http/Controllers/Api/ProductController.php

    public function index(Request $request)
    {
        $products = Product::filter($request->all())->paginate(15);
        return ProductResource::collection($products);
    }


  • Usage:

    bash
    # Example API request with filters GET /api/products?price=100&category=Electronics

  • 8. API Authentication with Sanctum

    Laravel Sanctum provides a simple way to authenticate your API with tokens.


  • Installation:

    composer require laravel/sanctum php artisan vendor:publish --provider="Laravel\Sanctum\SanctumServiceProvider" php artisan migrate

  • Middleware Setup:
  • // app/Http/Kernel.php

    'api' => [
        \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class,
        'throttle:api',
        \Illuminate\Routing\Middleware\SubstituteBindings::class,
    ],


  • Token-based Authentication:

  • // routes/api.php

    use Illuminate\Support\Facades\Route;

    Route::middleware('auth:sanctum')->get('/user', function (Request $request) {
        return $request->user();
    });


  • Generate Token:
  • // app/Http/Controllers/AuthController.php

    use App\Models\User;
    use Illuminate\Http\Request;

    class AuthController extends Controller
    {
        public function login(Request $request)
        {
            $credentials = $request->only('email', 'password');

            if (auth()->attempt($credentials)) {
                $user = auth()->user();
                $token = $user->createToken('API Token')->plainTextToken;

                return response()->json(['token' => $token], 200);
            }

            return response()->json(['error' => 'Unauthorized'], 401);
        }
    }
  • API Request with Token:
  • GET /api/user
    Authorization: Bearer <token>

  • 9. Versioning Your API
  • Versioning your API ensures that changes do not break existing clients. You can namespace your routes for versioning.

  • // routes/api.php

    Route::prefix('v1')->group(function () {
        Route::apiResource('products', ProductController::class);
    });


  • Benefits:

    • Versioning allows for backward compatibility as you make improvements to your API

  • No comments

    Theme images by fpm. Powered by Blogger.