andrey-helldar/api-response

Package for standardizing the responses from the API of your Symfony based applications.

v8.2.0 2021-07-06 18:00 UTC

README

Package for standardizing the responses from the API of your Symfony based applications.

api response

StyleCI Status Github Workflow Status Coverage Status Scrutinizer Code Quality

Stable Version Unstable Version Total Downloads License

Getting Started

Upgrade guides

[ to top ]

Installation

To get the latest version of API Response, simply require the project using Composer:

$ composer require andrey-helldar/api-response

This command will automatically install the latest version of the package for your environment.

Instead, you may of course manually update your require block and run composer update if you so choose:

{
    "require": {
        "andrey-helldar/api-response": "^8.0"
    }
}

Alright! Use api_response() helper.

Compatibility table

Package version PHP min version Symfony version Support Links
^8.0 7.2.5 ^4.0, ^5.0 Supported Upgrade guide
^7.0 7.2.5 ^4.0, ^5.0 Not Supported Upgrade guide
^6.0 7.3 ^4.0, ^5.0 Not Supported Upgrade guide
^5.0 7.1.3 ^4.0, ^5.0 Not Supported ---
^4.4.1 5.6.9 ^3.0, ^4.0, ^5.0 Not Supported ---
^4.0 5.6.9 ^3.0, ^4.0 Not Supported ---

[ to top ]

Using

Use with data key

as NULL with code:

// php 7.4 and below
return api_response(null, 304);

// php 8.0
return api_response( status_code: 304 );

return with code 304:

{
    "data": null
}

[ to top ]

as integer with default code:

return api_response(304);

return with code 200:

{
    "data": 304
}

[ to top ]

as string with default code:

return api_response('qwerty');

return with code 200:

{
    "data": "qwerty"
}

[ to top ]

as string with code:

return api_response('qwerty', 400);

return with code 400:

{
    "error": {
        "type": "Exception",
        "data": "qwerty"
    }
}

[ to top ]

as integer with code:

return api_response(304, 400);

return with code 400:

{
    "error": {
        "type": "Exception",
        "data": 304
    }
}

[ to top ]

as array:

$data = [
    [
        'title' => 'Title #1',
        'description' => 'Description #1',
    ],
    [
        'title' => 'Title #2',
        'description' => 'Description #2',
    ],
];

[ to top ]

as error

return api_response($data, 400);

return with code 400:

{
    "error": {
        "type": "Exception",
        "data": [
            {
                "title": "Title #1",
                "description": "Description #1"
            },
            {
                "title": "Title #2",
                "description": "Description #2"
            }
        ]
    }
}

[ to top ]

as success

return api_response($data);

return with code 200:

{
    "data": [
        {
            "title": "Title #1",
            "description": "Description #1"
        },
        {
            "title": "Title #2",
            "description": "Description #2"
        }
    ]
}

If the first parameter is a number, then the decryption of the error by code will be return. In other cases, the value of the passed variable will be return.

[ to top ]

with additional content

// php 7.4 and below
return api_response('title', 200, ['foo' => 'bar']);
// or
return api_response('title', null, ['foo' => 'bar']);

// php 8.0
return api_response('title', with: ['foo' => 'bar']);

return with code 200:

{
    "data": "title",
    "foo": "bar"
}

return with code 400:

{
    "error": {
        "type": "Exception",
        "data": "ok"
    },
    "foo": "bar"
}
return api_response(['data' => 'foo', 'bar' => 'baz']);

return with code 200:

{
    "data": "foo",
    "bar": "baz"
}

return with code 400:

{
    "error": {
        "type": "Exception",
        "data": "foo"
    },
    "bar": "baz"
}

[ to top ]

Use without data key

Since the goal of the package is to unify all the answers, we moved the variable definitions into a static function. So, for example, to enable or disable wrapping content in the data key, you need to call the wrapped or withoutWrap method:

use Helldar\ApiResponse\Services\Response;

Response::withoutWrap();

as NULL with code and without data key:

// php 7.4 and below
return api_response(null, 304);

// php 8.0
return api_response( status_code: 304 );

return with code 304:

[]

[ to top ]

as integer with default code and without data key:

return api_response(304, 200);

return with code 200:

304

[ to top ]

as string with default code and without data key:

return api_response('qwerty', 200);

return with code 200:

"qwerty"

[ to top ]

as string with code and without data key:

return api_response('qwerty', 400);

return with code 400:

{
    "error": {
        "type": "Exception",
        "data": "qwerty"
    }
}

[ to top ]

as integer with code and without data key:

return api_response(304, 400);

return with code 400:

{
    "error": {
        "type": "Exception",
        "data": 304
    }
}

[ to top ]

as array and without data key:

$data = [
    [
        'title' => 'Title #1',
        'description' => 'Description #1',
    ],
    [
        'title' => 'Title #2',
        'description' => 'Description #2',
    ],
];

as error and without data key

return api_response($data, 400);

return with code 400:

{
    "error": {
        "type": "Exception",
        "data": [
            {
                "title": "Title #1",
                "description": "Description #1"
            },
            {
                "title": "Title #2",
                "description": "Description #2"
            }
        ]
    }
}

[ to top ]

as success and without data key

return api_response($data, 200);
// or
return api_response($data);

return with code 200:

[
    {
        "title": "Title #1",
        "description": "Description #1"
    },
    {
        "title": "Title #2",
        "description": "Description #2"
    }
]

If the first parameter is a number, then the decryption of the error by code will be return. In other cases, the value of the passed variable will be return.

[ to top ]

with additional content and without data key:

// php 7.4 and below
return api_response('title', 200, ['foo' => 'bar']);

// php 8.0
return api_response('title', with: ['foo' => 'bar']);

return with code 200:

{
    "data": "title",
    "foo": "bar"
}

return with code 400:

{
    "error": {
        "type": "Exception",
        "data": "ok"
    },
    "foo": "bar"
}
return api_response(['data' => 'foo', 'bar' => 'baz']);

return with code 200:

{
    "data": "foo",
    "bar": "baz"
}

return with code 400:

{
    "error": {
        "type": "Exception",
        "data": "foo"
    },
    "bar": "baz"
}

[ to top ]

No extra data

In some cases, when returning answers, you must also give additional data. Such as stack trace, for example.

To prevent this data from getting in response to production, you can globally set a label to show or hide this data:

use Helldar\ApiResponse\Services\Response;

env('APP_DEBUG')
    ? Response::allowWith()
    : Response::withoutWith();

Now all responses will not contain the additional data being passed.

For example:

// php 7.4 and below
return api_response('title', 200, ['foo' => 'bar']);
// or
return api_response('title', null, ['foo' => 'bar']);

// php 8.0
return api_response('title', with: ['foo' => 'bar']);

return with code 200:

{
    "data": "title"
}

return with code 400:

{
    "error": {
        "type": "Exception",
        "data": "ok"
    }
}

Server Errors

Note: The $with parameter is also responsible for displaying server-side error messages.

In this case, Http errors will be displayed without masking.

For example:

use Helldar\ApiResponse\Services\Response;

Response::allowWith();

$e = new Exception('Foo', 0);

return api_response($e);

return with code 500:

{
    "error": {
        "type": "Exception",
        "data": "Foo"
    }
}

and

use Helldar\ApiResponse\Services\Response;

Response::withoutWith();

$e = new Exception('Foo', 0);

return api_response($e);

return with code 500:

{
    "error": {
        "type": "Exception",
        "data": "Whoops! Something went wrong."
    }
}

return with if code >=400 and < 500:

{
    "error": {
        "type": "Exception",
        "data": "Foo"
    }
}

[ to top ]

Returning exception instances

class FooException extends \Exception
{
    public function __construct()
    {
        parent::__construct('Foo', 405);
    }
}

class BarException extends \Exception
{
    public function __construct()
    {
        parent::__construct('Bar');
    }
}

$foo = new FooException();
$bar = new BarException();
return api_response($foo);

return with code 405:

{
    "error": {
        "type": "FooException",
        "data": "Foo"
    }
}
return api_response($foo, 408);

return with code 408:

{
    "error": {
        "type": "FooException",
        "data": "Foo"
    }
}
return api_response($bar);

return with code 400:

{
    "error": {
        "type": "BarException",
        "data": "Bar"
    }
}
return api_response($bar, 408);

return with code 408:

{
    "error": {
        "type": "BarException",
        "data": "Bar"
    }
}

You can also add additional data:

return api_response($foo, 405, ['foo' => 'Bar']);
// or
return api_response($foo, 0, ['foo' => 'Bar']);

return with code 405:

{
    "error": {
        "type": "FooException",
        "data": "Foo"
    },
    "foo": "Bar"
}

[ to top ]

Best practice use with the Laravel and Lumen Frameworks

If you use the Laravel or Lumen framework, you can update the extends in the app\Exceptions\Handler.php file depending on your application version and needs:

Version \ Type API + WEB Only API
8.x Helldar\ApiResponse\Exceptions\Laravel\Eight\Handler as ExceptionHandler Helldar\ApiResponse\Exceptions\Laravel\Eight\ApiHandler as ExceptionHandler
7.x Helldar\ApiResponse\Exceptions\Laravel\Seven\Handler as ExceptionHandler Helldar\ApiResponse\Exceptions\Laravel\Seven\ApiHandler as ExceptionHandler

If you did not add anything to this file, then delete everything properties and methods.

For example, as a result, a clean file will look like this:

<?php

namespace App\Exceptions;

use Helldar\ApiResponse\Exceptions\Laravel\Eight\Handler as ExceptionHandler;

class Handler extends ExceptionHandler
{
    //
}

More examples:

<?php

namespace App\Exceptions;

// use Helldar\ApiResponse\Exceptions\Laravel\Eight\Handler as ExceptionHandler;
use Helldar\ApiResponse\Exceptions\Laravel\Eight\ApiHandler as ExceptionHandler;

// use Helldar\ApiResponse\Exceptions\Laravel\Seven\Handler as ExceptionHandler;
// use Helldar\ApiResponse\Exceptions\Laravel\Seven\ApiHandler as ExceptionHandler;

class Handler extends ExceptionHandler
{
    //
}

Or you can change this file by adding code to it, similar to ours.

[ to top ]

Json Resources

Now, if you pass a resource object or validator object, it will also be rendered beautifully:

use Illuminate\Http\Resources\Json\JsonResource;

/** @mixin \Tests\Fixtures\Laravel\Model */
final class Resource extends JsonResource
{
    public function toArray($request)
    {
        return [
            'foo' => $this->foo,
            'bar' => $this->bar,
        ];
    }

    public function with($request)
    {
        return ['baz' => 'Baz'];
    }
}
$model = Model::first();
$resource = MyResource::make($model);

return api_response($resource);

return with code 200:

{
    "data": {
        "foo": "Foo",
        "bar": "Bar"
    },
    "baz": "Baz"
}

If Response::withoutWrap()

{
    "foo": "Foo",
    "bar": "Bar",
    "baz": "Baz"
}

If Response::withoutWith()

{
    "data": {
        "foo": "Foo",
        "bar": "Bar"
    }
}

If Response::withoutWith() and Response::withoutWrap()

{
    "foo": "Foo",
    "bar": "Bar"
}

[ to top ]

Validation

$data = [
    'foo' => 'Foo',
    'bar' => 123,
    'baz' => 'https://foo.example'
];

$rules = [
    'foo' => ['required'],
    'bar' => ['integer'],
    'baz' => ['sometimes', 'url'],
];
$validator = Validator::make($data, $rules);

return $validator->fails()
    ? new ValidationException($validator)
    : $validator->validated();

If success:

{
    "data": {
        "foo": "Foo",
        "bar": 123,
        "baz": "https://foo.example"
    }
}

If failed:

{
    "error": {
        "type": "ValidationException",
        "data": {
            "foo": ["The foo field is required."],
            "bar": ["The bar must be an integer."],
            "baz": ["The baz format is invalid."]
        }
    }
}

[ to top ]

License

This package is licensed under the MIT License.

For Enterprise

Available as part of the Tidelift Subscription.

The maintainers of andrey-helldar/api-response and thousands of other packages are working with Tidelift to deliver commercial support and maintenance for the open source packages you use to build your applications. Save time, reduce risk, and improve code health, while paying the maintainers of the exact packages you use. Learn more.

[ to top ]