anyo-lab/api-response

A Laravel package for universal API response and error handling

Installs: 5

Dependents: 0

Suggesters: 0

Security: 0

Stars: 0

Watchers: 0

Forks: 0

Open Issues: 0

pkg:composer/anyo-lab/api-response

v2.0.0 2026-02-23 17:24 UTC

This package is not auto-updated.

Last update: 2026-02-24 16:02:27 UTC


README

Latest Version on Packagist Total Downloads License Tests

A comprehensive Laravel package for universal API response and error handling. This package provides a standardized way to format API responses and handle exceptions in Laravel applications with multiple usage patterns and advanced features.

πŸš€ Features

  • 🎯 Standardized API Responses - Consistent response format across your entire API
  • πŸ›‘οΈ Automatic Exception Handling - Custom exception handler for API routes
  • πŸ“ Multiple Usage Methods - Use as service, trait, facade, macros, or helper functions
  • βš™οΈ Highly Configurable - Customize response structure and messages
  • πŸ“Š Built-in Pagination - Automatic pagination response formatting
  • πŸ”§ Laravel 9-12 Support - Compatible with multiple Laravel versions
  • 🎯 Resource Classes - Built-in resource transformation classes
  • πŸ”Œ Response Macros - Extend Laravel's response with API methods
  • 🌐 Global Helpers - Easy-to-use global helper functions
  • πŸ› οΈ Middleware Support - Automatic response formatting middleware
  • 🚦 Rate Limiting Integration - Formatted rate limiting responses
  • πŸ“¦ Response Compression - Automatic response compression (gzip, deflate)
  • πŸ”„ API Versioning - Built-in API versioning support
  • πŸ“ˆ Performance Monitoring - Response performance tracking

πŸ“‹ Requirements

  • PHP >= 8.1
  • Laravel >= 9.0

πŸ”§ Installation

Via Composer

composer require anyo-lab/api-response

Manual Installation

  1. Clone or download this package
  2. Add to your composer.json:
{
    "require": {
        "anyo-lab/api-response": "*"
    },
    "repositories": [
        {
            "type": "path",
            "url": "path/to/api-response"
        }
    ]
}
  1. Run composer update

βš™οΈ Configuration

Publish the configuration file:

php artisan vendor:publish --provider="ApiResponse\ApiResponseServiceProvider" --tag="api-response-config"

This will create config/api-response.php with customizable options.

Production readiness

Before using in a live project:

  1. Set APP_DEBUG=false in .env so error responses never expose stack traces or file paths.
  2. Publish config (optional): php artisan vendor:publish --tag=api-response-config to override messages, structure, or timestamp format.
  3. Override messages in config/api-response.php under messages so all default strings (e.g. "Resource not found") come from one place.
  4. Optional: Enable include_request_id and include_error_codes in config for tracing and machine-readable error codes.

All responses use the same config (structure, timestamp, error codes). ResponseHelper and resources use the same settings as the main ApiResponse service.

Upgrading / Breaking changes

  • Response caching removed: The package no longer provides api_cache(), api_remember(), or ApiResponseCache. For cached API responses, use Laravel’s Cache::remember() with api_success():

    use Illuminate\Support\Facades\Cache;
    
    $data = Cache::remember('users.list', 3600, fn () => User::all());
    return api_success($data, 'Success');
  • 204 No Content: noContent() now returns an empty response body (RFC 7231) instead of a JSON body.

  • Performance monitoring optional: Set register_performance_monitoring to false in config to disable cache usage when you do not use api_performance().

See CHANGELOG.md for full version history.

🎯 Quick Start

Basic Usage

use ApiResponse\ApiResponse;

class UserController extends Controller
{
    public function index(ApiResponse $apiResponse)
    {
        $users = User::paginate(10);
        
        return $apiResponse->paginated($users, 'Users retrieved successfully');
    }

    public function store(Request $request, ApiResponse $apiResponse)
    {
        $user = User::create($request->validated());
        
        return $apiResponse->created($user, 'User created successfully');
    }
}

Tip: For testability and flexibility, type-hint ApiResponseContract instead of ApiResponse so you can swap or mock the implementation.

πŸ“– Usage Methods

Method 1: Service Injection

use ApiResponse\ApiResponse;

class UserController extends Controller
{
    public function index(ApiResponse $apiResponse)
    {
        $users = User::paginate(10);
        
        return $apiResponse->paginated($users, 'Users retrieved successfully');
    }

    public function store(Request $request, ApiResponse $apiResponse)
    {
        $user = User::create($request->validated());
        
        return $apiResponse->created($user, 'User created successfully');
    }
}

Method 2: Trait Usage

use ApiResponse\Traits\ApiResponseTrait;

class UserController extends Controller
{
    use ApiResponseTrait;

    public function index()
    {
        $users = User::paginate(10);
        
        return $this->paginatedResponse($users, 'Users retrieved successfully');
    }

    public function store(Request $request)
    {
        $user = User::create($request->validated());
        
        return $this->createdResponse($user, 'User created successfully');
    }
}

Method 3: Facade Usage

use ApiResponse\Facades\ApiResponse;

class UserController extends Controller
{
    public function index()
    {
        $users = User::paginate(10);
        
        return ApiResponse::paginated($users, 'Users retrieved successfully');
    }

    public function store(Request $request)
    {
        $user = User::create($request->validated());
        
        return ApiResponse::created($user, 'User created successfully');
    }
}

Method 4: Response Macros

class UserController extends Controller
{
    public function index()
    {
        $users = User::paginate(10);
        
        return response()->apiPaginated($users, 'Users retrieved successfully');
    }

    public function store(Request $request)
    {
        $user = User::create($request->validated());
        
        return response()->apiCreated($user, 'User created successfully');
    }
}

Method 5: Global Helper Functions

class UserController extends Controller
{
    public function index()
    {
        $users = User::paginate(10);
        
        return api_paginated($users, 'Users retrieved successfully');
    }

    public function store(Request $request)
    {
        $user = User::create($request->validated());
        
        return api_created($user, 'User created successfully');
    }
}

Method 6: Resource Classes

use ApiResponse\Resources\ApiResource;
use Illuminate\Http\Request;

class UserResource extends ApiResource
{
    protected function transformData(Request $request): array
    {
        return [
            'id' => $this->id,
            'name' => $this->name,
            'email' => $this->email,
            'created_at' => $this->created_at?->toISOString(),
        ];
    }

    protected function getMessage(): string
    {
        return 'User retrieved successfully';
    }
}

class UserController extends Controller
{
    public function show($id)
    {
        $user = User::findOrFail($id);
        
        return new UserResource($user);
    }

    public function index()
    {
        $users = User::paginate(10);
        
        return UserResource::collection($users);
    }
}

πŸ“€ Response Methods

Success Responses

// Basic success response
$apiResponse->success($data, 'Success message');

// Created response (201)
$apiResponse->created($data, 'Resource created');

// No content response (204)
$apiResponse->noContent('No content available');

// Paginated response
$apiResponse->paginated($paginator, 'Data retrieved');

// Collection response
$apiResponse->collection($collection, 'Data retrieved');

// Resource response
$apiResponse->resource($resource, 'Resource retrieved');

Error Responses

// Basic error response
$apiResponse->error('Error message', 400);

// Validation error (422)
$apiResponse->validationError($errors, 'Validation failed');

// Not found (404)
$apiResponse->notFound('Resource not found');

// Unauthorized (401)
$apiResponse->unauthorized('Authentication required');

// Forbidden (403)
$apiResponse->forbidden('Access denied');

// Server error (500)
$apiResponse->serverError('Internal server error');

// Bad request (400)
$apiResponse->badRequest('Invalid request');

// Conflict (409)
$apiResponse->conflict('Resource conflict');

// Too many requests (429)
$apiResponse->tooManyRequests('Rate limit exceeded');

πŸ“‹ Response Format

All responses follow this standardized format:

Success Response

{
    "success": true,
    "message": "Success message",
    "data": {
        "id": 1,
        "name": "John Doe",
        "email": "john@example.com"
    },
    "errors": null,
    "meta": null,
    "timestamp": "2024-01-01T12:00:00.000000Z"
}

Error Response

{
    "success": false,
    "message": "Error message",
    "data": null,
    "errors": {
        "email": ["The email field is required."],
        "password": ["The password must be at least 8 characters."]
    },
    "meta": null,
    "timestamp": "2024-01-01T12:00:00.000000Z"
}

Pagination Response

{
    "success": true,
    "message": "Data retrieved successfully",
    "data": [
        {
            "id": 1,
            "name": "John Doe"
        }
    ],
    "errors": null,
    "meta": {
        "current_page": 1,
        "per_page": 10,
        "total": 100,
        "last_page": 10,
        "from": 1,
        "to": 10,
        "has_more_pages": true
    },
    "timestamp": "2024-01-01T12:00:00.000000Z"
}

πŸ›‘οΈ Exception Handling

The package includes a custom exception handler that automatically formats exceptions for API routes:

Exception Status Code Description
ValidationException 422 Validation errors
AuthenticationException 401 Unauthorized
ModelNotFoundException 404 Not found
NotFoundHttpException 404 Not found
QueryException 500 Server error (with debug info in development)
HttpException Respective HTTP status code

βš™οΈ Configuration Options

Response Structure

'structure' => [
    'success' => 'success',
    'message' => 'message',
    'data' => 'data',
    'errors' => 'errors',
    'meta' => 'meta',
    'timestamp' => 'timestamp',
],

Default Messages

'messages' => [
    'success' => 'Success',
    'created' => 'Resource created successfully',
    'updated' => 'Resource updated successfully',
    'deleted' => 'Resource deleted successfully',
    'not_found' => 'Resource not found',
    'unauthorized' => 'Unauthorized',
    'forbidden' => 'Forbidden',
    'validation_failed' => 'Validation failed',
    'server_error' => 'Internal server error',
    'bad_request' => 'Bad request',
    'conflict' => 'Conflict occurred',
    'too_many_requests' => 'Too many requests',
],

Other Options

'include_timestamp' => true,
'timestamp_format' => 'Y-m-d H:i:s',
'debug' => env('APP_DEBUG', false),
'use_exception_handler' => true,
'api_routes_pattern' => 'api/*',

πŸ”„ Advanced Features

Middleware Support

// In your routes/api.php
Route::middleware(['api.response'])->group(function () {
    Route::get('/users', [UserController::class, 'index']);
    Route::post('/users', [UserController::class, 'store']);
});

// Or register globally in App\Http\Kernel.php
protected $middlewareGroups = [
    'api' => [
        \ApiResponse\Http\Middleware\ApiResponseMiddleware::class,
        // ... other middleware
    ],
];

API Versioning

use ApiResponse\Services\ApiVersioningService;

class UserController extends Controller
{
    public function index(ApiVersioningService $versioning)
    {
        $version = $versioning->getVersion(request());
        
        if ($version === 'v2') {
            // Return v2 format
            return $this->paginatedResponseV2($users);
        }
        
        // Return v1 format
        return $this->paginatedResponse($users);
    }
}

Response Compression

use ApiResponse\Services\ResponseCompressionService;

class UserController extends Controller
{
    public function index(ResponseCompressionService $compression)
    {
        $response = $this->paginatedResponse($users);
        
        return $compression->compress(request(), $response);
    }
}

Performance Monitoring

use ApiResponse\Services\PerformanceMonitoringService;

class UserController extends Controller
{
    public function index(PerformanceMonitoringService $monitoring)
    {
        $monitoring->startMonitoring(request());

        $users = User::paginate(10);
        $response = $this->paginatedResponse($users);

        $monitoring->endMonitoring(request(), $response);

        return $response;
    }
}

πŸ§ͺ Testing

<?php

namespace Tests\Feature;

use Tests\TestCase;
use App\Models\User;

class UserApiTest extends TestCase
{
    public function test_can_get_users()
    {
        $response = $this->getJson('/api/users');

        $response->assertStatus(200)
                ->assertJsonStructure([
                    'success',
                    'message',
                    'data',
                    'meta',
                    'timestamp'
                ])
                ->assertJson([
                    'success' => true,
                    'message' => 'Users retrieved successfully'
                ]);
    }

    public function test_can_create_user()
    {
        $userData = [
            'name' => 'John Doe',
            'email' => 'john@example.com',
            'password' => 'password123'
        ];

        $response = $this->postJson('/api/users', $userData);

        $response->assertStatus(201)
                ->assertJson([
                    'success' => true,
                    'message' => 'User created successfully'
                ]);
    }

    public function test_validation_errors()
    {
        $response = $this->postJson('/api/users', []);

        $response->assertStatus(422)
                ->assertJson([
                    'success' => false,
                    'message' => 'Validation failed'
                ])
                ->assertJsonStructure([
                    'errors' => [
                        'name',
                        'email',
                        'password'
                    ]
                ]);
    }
}

πŸ“Š Complete Example

<?php

namespace App\Http\Controllers;

use App\Models\User;
use Illuminate\Http\Request;
use ApiResponse\Traits\ApiResponseTrait;
use Illuminate\Validation\ValidationException;
use Illuminate\Database\Eloquent\ModelNotFoundException;

class UserController extends Controller
{
    use ApiResponseTrait;

    public function index()
    {
        try {
            $users = User::paginate(10);
            return $this->paginatedResponse($users, 'Users retrieved successfully');
        } catch (\Exception $e) {
            return $this->serverErrorResponse('Failed to retrieve users');
        }
    }

    public function store(Request $request)
    {
        try {
            $validated = $request->validate([
                'name' => 'required|string|max:255',
                'email' => 'required|email|unique:users',
                'password' => 'required|min:8',
            ]);

            $user = User::create($validated);
            return $this->createdResponse($user, 'User created successfully');
        } catch (ValidationException $e) {
            return $this->validationErrorResponse($e->errors(), 'Validation failed');
        } catch (\Exception $e) {
            return $this->serverErrorResponse('Failed to create user');
        }
    }

    public function show($id)
    {
        try {
            $user = User::findOrFail($id);
            return $this->resourceResponse($user, 'User retrieved successfully');
        } catch (ModelNotFoundException $e) {
            return $this->notFoundResponse('User not found');
        }
    }

    public function update(Request $request, $id)
    {
        try {
            $user = User::findOrFail($id);
            $validated = $request->validate([
                'name' => 'sometimes|string|max:255',
                'email' => 'sometimes|email|unique:users,email,' . $id,
            ]);

            $user->update($validated);
            return $this->successResponse($user, 'User updated successfully');
        } catch (ModelNotFoundException $e) {
            return $this->notFoundResponse('User not found');
        } catch (ValidationException $e) {
            return $this->validationErrorResponse($e->errors(), 'Validation failed');
        }
    }

    public function destroy($id)
    {
        try {
            $user = User::findOrFail($id);
            $user->delete();
            return $this->successResponse(null, 'User deleted successfully');
        } catch (ModelNotFoundException $e) {
            return $this->notFoundResponse('User not found');
        }
    }
}

Testing this package

composer install
composer test           # run PHPUnit
composer format:check   # Laravel Pint
composer analyse        # PHPStan

Enterprise & production

For production use, enable request IDs and error codes in config; apply rate limiting and set APP_DEBUG=false.

🀝 Contributing

Please see CONTRIBUTING.md for development setup, code style, and pull request guidelines.

Security

If you discover a security vulnerability, please see SECURITY.md.

Changelog

See CHANGELOG.md for version history.

πŸ“„ License

This package is open-sourced software licensed under the MIT license.

πŸ†˜ Support

πŸ™ Acknowledgments

  • Laravel team for the amazing framework
  • All contributors who help improve this package
  • The open-source community for inspiration and feedback

Laravel API Response – A generic package for standardized API responses.