Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Laravel Api Response Laravel Package

guanguans/laravel-api-response

Laravel package for building consistent JSON API responses. It provides helpers and macros for uniform success and error envelopes with customizable code, message, data, and HTTP status, plus exception rendering to keep API output predictable.

View on GitHub
Deep Wiki
Context7

Getting Started

Install the package and publish its config so you can see the response envelope before writing any code:

composer require guanguans/laravel-api-response
php artisan vendor:publish --tag=api-response-config

The first place to look is config/api-response.php. It defines the envelope keys (code, message, data), the default success/failure codes and messages, and any wrapping behaviour. That file is the contract your frontend will depend on, so agree on it before building endpoints. After that, read the facade (Guanguans\LaravelApiResponse\Facades\ApiResponse) and the Response class it returns — the facade is the primary entry point, and the Response object is what Laravel serialises.

The first use case is almost always "make one endpoint return a predictable shape":

use Guanguans\LaravelApiResponse\Facades\ApiResponse;

Route::get('/ping', fn () => ApiResponse::success(['pong' => true]));

Which produces the standard envelope:

{ "code": 200, "message": "OK", "data": { "pong": true } }

Then prove the failure path works too:

return ApiResponse::failure('Resource not found', 404);

If both of those render as expected, the package is wired up correctly and you can standardise the rest of the API around it.

Implementation Patterns

Use the response object as a controller return value. The returned Response implements Laravel's Responsable contract, so you can return it directly from controllers, route closures, and form requests. This keeps controllers free of response()->json([...]) boilerplate:

public function show(User $user)
{
    return ApiResponse::success(new UserResource($user));
}

Add thin helpers to a base controller rather than repeating message strings and codes:

protected function ok(mixed $data = null, string $message = 'OK')
{
    return ApiResponse::success($data, $message);
}

protected function fail(string $message, int $code = 400, mixed $data = null)
{
    return ApiResponse::failure($message, $code, $data);
}

Centralise exception rendering. Map domain exceptions to envelope responses in bootstrap/app.php (Laravel 11+) or app/Exceptions/Handler.php (older). This is where the package earns its keep — every uncaught exception gets the same shape as successful responses:

$this->renderable(function (Throwable $e, Request $request) {
    if ($request->expectsJson()) {
        return ApiResponse::failure(
            app()->isProduction() ? 'Internal Server Error' : $e->getMessage(),
            $e instanceof ModelNotFoundException ? 404 : 500
        );
    }
});

Normalise validation failures in the same handler so clients always parse code / message / data. ValidationException should return 422 with the errors under data (or a dedicated errors key), not a raw Laravel error payload.

Use attributes for per-endpoint overrides if the package version you install exposes them — they keep status codes and messages next to the action instead of buried in a config array:

#[ApiResponse(statusCode: 201, message: 'Created')]
public function store(StoreUserRequest $request) { /* ... */ }

Check the installed version's attribute namespace; this is the part of the API most likely to shift between releases.

Pass paginators and resources through the package, not around it. Returning ApiResponse::success($users) where $users is a paginator lets the package merge data and pagination meta. If you manually call ->items() you'll lose the meta unless you add it yourself.

Assert the envelope in tests. A single assertion trait or helper keeps the whole suite consistent:

$this->getJson('/api/users')
    ->assertOk()
    ->assertJsonPath('code', 200)
    ->assertJsonStructure(['code', 'message', 'data']);

Treat the envelope as a frontend contract. Once agreed, client interceptors only need to branch on HTTP status and the business code. Document both, because they are not the same thing.

Gotchas and Tips

  • Two status layers. HTTP status describes transport; code describes domain outcome. Decide early whether code mirrors HTTP or is a business code (e.g. 10001). Mixing them makes clients branch on both inconsistently.
  • Double wrapping is the most common bug. Returning a JsonResource inside ApiResponse::success() is fine; returning $resource->response() wraps the already-serialised response again. Pick one path per endpoint.
  • Middleware scope. If you add the package's response middleware globally, it will also touch non-API routes and already-wrapped responses. Register it on the api group only, and make it idempotent.
  • Don't swallow every exception. A blanket catch (Throwable) that returns 200 hides real 500s and breaks monitoring. Re-throw unknown exceptions after logging, or at least preserve the original status.
  • Leaking exception messages. Gate $e->getMessage() on app()->isProduction(); the package will faithfully render whatever you give it.
  • Empty data semantics. data: [], data: null, and an omitted data key are three different contracts. Pick one and assert it in tests — frontends often break on the difference.
  • Config cache. After editing config/api-response.php, run php artisan config:clear. In CI, always clear before running tests.
  • Localisation. Default messages may be translation keys. If you need Chinese/English parity, publish the language files and confirm what message actually contains in the JSON.
  • Debugging. dd($response->toArray()) (or ->getData(true)) before it hits the HTTP layer; and log the original throwable in the handler before converting it, or you lose the stack trace.
  • Extension points. Prefer the published config and the facade over editing vendor files. If you need a custom payload shape, bind your own response class in the container or use the package's macro support rather than forking.
  • Version pinning. With a small package and a fast release cadence, pin a minor version in composer.json and read the changelog before upgrading — attribute names and helper methods are the usual breaking points.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
shitoudev/permissionmanager
johannesclimacus/laravel-tui
hocvt/seotools
vitalybaev/sypexgeo
amphibee/themosis-ignition
gito-me/fast-excel
frankyso/laravel-ipaymu
geolize-studio/laravel-chile
boomerangui/chat
cgv/localized-resource
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox
pairus/product-data
fayne/laravel-filament-cms-core