laravel-api-response-builder

repository·master·Indexed 21 days ago

https://github.com/marcinorlowski/laravel-api-response-builder

A Laravel package for building standardized, normalized, and predictable REST API JSON responses. It provides a structured way to create consistent data structures and reliable error handling, featuring an ExceptionHandlerHelper to prevent HTML error pages, automatic on-the-fly data conversion for Eloquent models and collections, and built-in PHPUnit testing support. The package supports Laravel versions 9.4 through 12.

Tokens
12.2K
Snippets
40
Records
55
Agent score
70%

What's inside laravel-api-response-builder

  1. Overview of ResponseBuilder

    master

    ResponseBuilder is a lightweight Laravel package designed to help developers build normalized, predictable, and easy-to-consume REST API JSON responses. It is built with API consumers in mind, providing a consistent JSON structure that makes client-side integration effortless.

    Key benefits include:

    • No dependencies: Keeps your project lightweight.
    • Predictable Structure: Well-defined JSON responses that are easy for clients to parse.
    • Robust Error Handling: Includes an ExceptionHandlerHelper to ensure the API communicates in JSON even during unexpected exceptions (preventing HTML error pages).
    • Testing Support: Provides traits for easy PHPUnit-based testing of your API responses.
    • Advanced Features: Supports on-the-fly data conversion, localization, and API chaining.
  2. Overview of ResponseBuilder for Laravel

    master
    The ResponseBuilder library is a helper for the Laravel framework designed to facilitate the creation of clean, normalized, and easy-to-consume JSON response structures for REST APIs. It provides a structured way to build API responses rather than manually constructing arrays or using standard Laravel response helpers.
  3. Understand the standard JSON response structure

    master

    The ResponseBuilder guarantees a consistent JSON structure for all responses. By default, every response contains the following keys:

    • success (boolean): Indicates if the API method succeeded or failed.
    • code (int): Your custom return code (typically used for errors).
    • locale (string): The locale used for the message (automatically retrieved via \App::getLocale()).
    • message (string): A human-readable explanation of the code value.
    • data (object|null): Contains any additional data. If no extra data is provided, this key remains present with a null value.

    Note: To return elements outside of this structure (not within the data key), refer to the Manipulating Response Object documentation.

    {
      "success": true,
      "code": 0,
      "locale": "en",
      "message": "OK",
      "data": null
    }
  4. How on-the-fly data conversion works

    master

    The ResponseBuilder can automatically convert complex objects into their array/JSON representations. When you pass an object to withData() or use it within a response structure, the builder checks if the object belongs to a supported class. If it does, it converts it automatically, saving you from manual toArray() calls.

    Supported classes out of the box include:

    • \Illuminate\Database\Eloquent\Model
    • \Illuminate\Support\Collection
    • \Illuminate\Database\Eloquent\Collection
    • \Illuminate\Http\Resources\Json\JsonResource
    • \Illuminate\Pagination\LengthAwarePaginator
    • \Illuminate\Pagination\Paginator
  5. Configure and use API code ranges

    master

    To support chained API calls and prevent error code collisions, ResponseBuilder supports code ranges using min_code and max_code in your configuration. This ensures that each API in a chain returns unique, non-overlapping error codes.

    Important Constraints:

    • The first 20 codes (from 0 to 19 inclusive) are reserved for ResponseBuilder internals and must not be used or assigned to your own codes.
    • Code 0 is reserved for success and cannot be used with error() methods.
    • Attempting to return a code outside your configured range will throw an exception.
    • If you do not need code ranges, set max_code in your configuration to a very high value.
  6. Configure custom API error codes for exceptions

    master

    While ExceptionHandlerHelper works out of the box, it is recommended to map exceptions to your own custom API codes. This allows you to identify which module triggered the error.

    1. Define constants in your ApiCodes class (e.g., app/ApiCodes.php) within your allowed code range.
    2. Map these constants to specific exception types in config/response_builder.php under the exception_handler.exception key.
    // In your ApiCodes class
    public const HTTP_NOT_FOUND = ...;
    
    // In config/response_builder.php
    'exception_handler' => [
        'exception' => [
            'http_not_found' => ['code' => ApiCode::HTTP_NOT_FOUND],
            // ... other mappings
        ],
    ],
  7. Check PHP and Laravel version compatibility

    master

    The ResponseBuilder library is tightly coupled with specific PHP and Laravel versions. Each major version of ResponseBuilder corresponds to a specific Laravel version, which in turn dictates the minimum required PHP version.

    Even if the library code is technically compatible with older PHP versions, you must adhere to the requirements of the specific ResponseBuilder major version you are using to ensure compatibility with the underlying Laravel version.

    Example: ResponseBuilder v12 requires PHP 8.2+ (because Laravel v12 requires PHP 8.2+).
  8. Testing API responses with ApiResponse and TestingHelpers

    master

    You can validate the structure and content of your Laravel API responses using the ApiResponse class and the TestingHelpers trait.

    When you wrap a response in ApiResponse::fromJson($response->getContent()), the library validates the response structure. Because ApiResponse is a type-hinted class, it handles data types for core elements like code or language automatically.

    Note: While the library validates the type of the data node, it does not validate the specific content inside the data node. You are responsible for asserting the specific data values yourself.

    <?php
    
    use MarcinOrlowski\ResponseBuilder\ApiResponse;
    use MarcinOrlowski\ResponseBuilder\Tests\Traits\TestingHelpers;
    
    class LoginTest extends \Illuminate\Foundation\Testing\TestCase
    {
        use TestingHelpers;
    
        public function testLogin(): void
        {
            // Call your method under test.
            $response = $this->call('POST', '/v1/session/foo');
    
            // Get the response validated and processed.
            $api = ApiResponse::fromJson($response->getContent());
    
            // Add some tests of your choice.
            $this->assertTrue($api->success());
        }
    }
  9. Install laravel-api-response-builder via Composer

    master

    Install the package using Composer. You can install the latest version or specify a specific version using the MAJOR.MINOR format to lock the dependency to a specific release and its bug-fixing patches.

    # Install the latest version
    composer require marcin-orlowski/laravel-api-response-builder
    
    # Install a specific version (e.g., 9.3)
    composer require marcin-orlowski/laravel-api-response-builder:9.3
  10. Unit testing your ApiCodes

    master

    To ensure your ApiCodes class and configuration are healthy, you can use the ApiCodesHelpers and ApiCodesTests traits. These traits automatically validate:

    • If the codes range is set correctly.
    • If all codes defined in your ApiCodes class have a corresponding mapping entry in your config.
    • If all codes are within the allowed range.
    • If all defined ApiCodes constant values are unique.
    • If all codes are mapped to existing locale strings.

    To implement this without polluting your production ApiCodes class, follow these steps:

    1. Create a test-only class (e.g., TestableApiCodes) that extends your production ApiCodes class and uses the MarcinOrlowski\ResponseBuilder\ApiCodesHelpers trait.
    2. Create a PHPUnit test class that uses the MarcinOrlowski\ResponseBuilder\Tests\Traits\ApiCodesTests trait.
    3. Implement the getApiCodesClassName(): string method in your test class to return the name of your test-only class.
    <?php
    
    use MarcinOrlowski//
    use MarcinOrlowski\ResponseBuilder\ApiCodesHelpers;
    
    // 1. Create a test-only class to avoid polluting production code
    class TestableApiCodes extends \App\ApiCodes
    {
        use ApiCodesHelpers;
    }
    
    use MarcinOrlowski\ResponseBuilder\Tests\Traits\ApiCodesTests;
    
    // 2. Create the PHPUnit test class
    class AppCodesTest extends TestCase
    {
        use ApiCodesTests;
    
        // 3. Provide the name of the testable class
        public function getApiCodesClassName(): string
        {
            return TestableApiCodes::class;
        }
    }