qeezer / api-responder
The api responder.
Requires
- php: ^7.2 || ^8
- ext-json: *
- symfony/http-foundation: ^4.3.4 || ^5.1 || ^6
Requires (Dev)
- phpstan/phpstan: ^1.10
- phpunit/phpunit: ^9.5
- symfony/http-kernel: ^5.4 || ^6
Suggests
- illuminate/contracts: Needed by Helpers::laravelExceptionRender for Laravel/Lumen exception rendering
- illuminate/pagination: Needed by LaravelPaginatorAdapter when passing native Laravel paginators to responsePaginate
- symfony/http-kernel: Needed by Helpers::laravelExceptionRender to recognize Symfony HttpException instances
README
Fast build php web api response. Support laravel/lumen framework. Not support hyperf.
install
composer require qeezer/api-responder ^1.0
usage
use QeeZer\ApiResponder\ResponderFactory
<?php namespace App\Controllers; use QeeZer\ApiResponder\ResponderFactory; use Request; use App\Models\User; class IndexController { public function index(Request $request) { return ResponderFactory::responseSuccess(); } public function item(Request $request) { return ResponderFactory::responseItem(User::find(1)); } public function collection(Request $request) { return ResponderFactory::responseCollection(User::all()); } public function paginate(Request $request) { return ResponderFactory::responsePaginate(User::simplePaginate(10)); } public function data(Request $request) { return ResponderFactory::responseData([ 'is_can' => true, 'name' => 'Tom', ]); } public function user(Request $request) { if (!$request->user()) { return ResponderFactory::responseUnauthorized(); } return ResponderFactory::responseItem($request->user()); } }
use QeeZer\ApiResponder\Responder
app/Controllers/BaseController.php
<?php namespace App\Controllers; use QeeZer\ApiResponder\Responder; use Request; class BaseController { use Responder; }
app/Controllers/IndexController.php
<?php namespace App\Controllers; use QeeZer\ApiResponder\Responder; use Request; class IndexController extends BaseController { public function index(Request $request) { return $this->responseSuccess(); } }
use response resource
app/Resources/UserResource.php
<?php namespace App\Resources; use QeeZer\ApiResponder\DefaultResource; class UserResource extends DefaultResource { public function toArray(): array { return [ 'id' => $this->data['id'], 'name' => $this->data['name'], 'phone' => $this->data['mobile'], ]; } }
app/Controllers/UserController.php
<?php namespace App\Controllers; use App\Models\User; use App\Resources\UserResource; use QeeZer\ApiResponder\Responder; use Request; class IndexController extends BaseController { public function detail(Request $request) { $data = $this->validata([ 'uid' => 'required|int' ], $request->all()); return $this->responseItem(User::find($data['uid']), UserResource::class); } }
response list
ResponderFactory::responseItem(); ResponderFactory::responseCollection(); ResponderFactory::responsePaginate(); ResponderFactory::responseData(); ResponderFactory::responseSuccess(); ResponderFactory::responseFail(); ResponderFactory::responseError(); ResponderFactory::responseUnauthorized(); ResponderFactory::responseCreated();
in laravel or lumen
app/Exceptions/Handler.php
public function render($request, $exception) { // other return \QeeZer\ApiResponder\Helpers::laravelExceptionRender($request, $exception); }
response shape
{
"code": 0,
"message": "ok",
"data": {
"meta": null,
"data": null
}
}
code vs HTTP status convention
responseSuccess/responseData/responseItem/responseCollection/responsePaginate: HTTP 200, businesscode = 0.responseCreated: HTTP 201, businesscode = 0.responseFail: HTTP 200 by default, businesscode = 1(or a caller-suppliederrCode). Use this for expected business failures that the client should handle by business code, not HTTP status.responseUnauthorized: HTTP 401, businesscode = 401.responseHttp: HTTP status = businesscode(a thin pass-through).responseError(Throwable):BusinessExceptionimplementations keep the caller's message/code and stay at HTTP 200 (it's a business error, not a server fault).- Any other
Throwableis treated as a server error: HTTP 500. Itscodedefaulting to0is collapsed onto the fail code1(so it never collides with the success code0); string codes are preserved verbatim and also prefixed onto the message as[code]message.
exception detail exposure
Exception internals (message/file/line/trace) are only attached to the response for non-BusinessException throwables, and only when debug exposure is on. Control it explicitly:
\QeeZer\ApiResponder\Helpers::exposeExceptionInfo(true); // force on \QeeZer\ApiResponder\Helpers::exposeExceptionInfo(false); // force off
With no explicit override, it falls back to Laravel/Lumen's config('app.debug') when available, and otherwise defaults to off.
paginate with non-Laravel paginators
responsePaginate accepts any QeeZer\ApiResponder\Contracts\PaginatorAdapter, or auto-wraps a native Laravel paginator via LaravelPaginatorAdapter. For Hyperf or a custom backend, implement PaginatorAdapter:
class MyPaginatorAdapter implements \QeeZer\ApiResponder\Contracts\PaginatorAdapter { public function getItems(): array { /* ... */ } public function getPagination(): array { /* ... */ } public function getLinks(): array { /* ... */ } } return ResponderFactory::responsePaginate(new MyPaginatorAdapter());
responseCollection follows the same shape: a CollectionAdapter, a plain array, or a Laravel-style collection (auto-wrapped via LaravelCollectionAdapter).
run tests
composer test— PHPUnit suitecomposer analyze— PHPStan static analysis (level 5)- CI runs both on PHP 7.4 → 8.3 (
.github/workflows/tests.yml)