Skip to content

The objective of this package is to facilitate error outputs from API requests in accordance with the RFC 9457 standard.

License

Notifications You must be signed in to change notification settings

pedrosalpr/laravel-api-problem

Repository files navigation

Laravel Api Problem

Latest Version on Packagist GitHub Tests Action Status GitHub Code Style Action Status Total Downloads

The objective of this package is to facilitate error outputs from API requests in accordance with the RFC 9457 standard.

It transforms error outputs into json format with the following characteristics:

  • header:
    • Content-Type: application/problem+json
  • response:
    • type: URI that identifies the type of error that occured, for example https://example.com/validation-error.
    • title: Human-readable identifier, usually the same type field should have the same title field alongside. An example would be something like Form validation failed.
    • status: A copy of the HTTP status code.
    • detail: More information about the specific problem, and if it's appropriate also steps to correct it. For example information about a form validation problem Username is already taken, please choose a different username.
    • instance: An identifier for this specific occurence, which may not be useful to the client but may be included in a bug report for example.
    • Additional fields: Any fields may be added to give additional information, and consuming clients are expected to ignore any that they don't have specific support for.
      • timestamp: (RFC 9457 enhancement): A timestamp indicating when the problem was generated. This helps in logging and tracing the error, especially in systems where timing is critical.

Example:

Server Response:

HTTP/1.1 422 Unprocessable Entity
Content-Type: application/problem+json
Content-Language: en
{
     "status": 422,
     "type": "https://example.test/validation-error",
     "title": "Form validation failed",
     "detail": "One or more fields have validation errors. Please check and try again.",
     "instance": "http://example.test/api/test/1",
     "timestamp": "2024-02-09T11:55:51.252968Z",
     "errors": [
        {
            "name": "username",
            "reason": "Username is already taken."
        },
        {
            "name": "email",
            "reason": "Email format is invalid."
        }
    ]
}

Installation

You can install the package via composer:

composer require pedrosalpr/laravel-api-problem

Usage

Default Mode (Older Version Laravel 9 and 10)

To use it, just go to the register method within Exceptions\Handler.php and add the following code:

use Pedrosalpr\LaravelApiProblem\LaravelApiProblem;

public function register(): void
{
    ...

    $this->renderable(function (\Throwable $e, Request $request) {
        if ($request->is('api/*') || $this->shouldReturnJson($request, $e)) {
            $apiProblem = new LaravelApiProblem($e, $request);
            return $apiProblem->render();
        }
    });
}

If you want to debug, just add the following line before the return:

dd($apiProblem->toDebuggableArray());

Default Mode (Laravel 11)

To use it, add the following code to the Exception Closure in the bootstrap/app.php file:

use Pedrosalpr\LaravelApiProblem\LaravelApiProblem;

    ...

    ->withExceptions(function (Exceptions $exceptions) {
        $exceptions->render(function (\Throwable $e, Request $request) {
            if ($request->is('api/*') || $this->shouldReturnJson($request, $e)) {
                $apiProblem = new LaravelApiProblem($e, $request);
                return $apiProblem->render();
            }
        });
    })...

Creating Exceptions in the Api Problem pattern

There is the possibility of creating exceptions that extend LaravelApiProblemException.

This already makes it easier to transform the exception into the Api Problem pattern.

To do this, simply run the following command:

php artisan laravel-api-problem:exception {name}

For example:

php artisan laravel-api-problem:exception DummyException

This creates a class exception inside Exceptions\ApiProblem;

<?php

namespace App\Exceptions\ApiProblem;

use Pedrosalpr\LaravelApiProblem\Exceptions\LaravelApiProblemException;

class DummyException extends LaravelApiProblemException
{

}

Custom Mode

If you want to customize an Api Problem class to add your guidelines for which error responses should be returned, simply extend the class with the following command:

php artisan laravel-api-problem:extend

For example:

php artisan laravel-api-problem:extend DummyApiProblem

This creates a class Api Problem inside ApiProblem;

<?php

namespace App\ApiProblem;

use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Pedrosalpr\LaravelApiProblem\Http\LaravelHttpApiProblem;
use Pedrosalpr\LaravelApiProblem\LaravelApiProblem;

class DummyApiProblem extends LaravelApiProblem
{
    public function __construct(
        protected \Throwable $exception,
        protected Request $request
    ) {
        match (get_class($exception)) {
            \Exception::class => $this->dummy(),
            default => parent::__construct($exception, $request)
        };
    }

    protected function dummy()
    {
        $extensions = [
            'errors' => "Dummy",
        ];
        $this->apiProblem = new LaravelHttpApiProblem(
            Response::HTTP_I_AM_A_TEAPOT,
            $this->exception->getMessage(),
            $this->getUriInstance(),
            $extensions
        );
    }
}

And within the match, add the names of the exceptions classes with their respective methods, such as dummy().

Older Version Laravel 9 and 10

In the Handler.php file replace the LaravelApiProblem object instance to DummyApiProblem.

    $this->renderable(function (\Throwable $e, Request $request) {
        if ($request->is('api/*') || $this->shouldReturnJson($request, $e)) {
            $apiProblem = new DummyApiProblem($e, $request);
            return $apiProblem->render();
        }
    });

Laravel 11

Add the following code to the Exception Closure in the bootstrap/app.php file:

    ->withExceptions(function (Exceptions $exceptions) {
        $exceptions->render(function (\Throwable $e, Request $request) {
            if ($request->is('api/*') || $this->shouldReturnJson($request, $e)) {
                $apiProblem = new DummyApiProblem($e, $request);
                return $apiProblem->render();
            }
        });
    })

Testing

TODO

Changelog

Please see CHANGELOG for more information on what has changed recently.

Contributing

Please see CONTRIBUTING for details.

Security Vulnerabilities

Please review our security policy on how to report security vulnerabilities.

Credits

License

The MIT License (MIT). Please see License File for more information.

About

The objective of this package is to facilitate error outputs from API requests in accordance with the RFC 9457 standard.

Topics

Resources

License

Stars

Watchers

Forks

Sponsor this project

Packages

No packages published