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

kennedy-osaze/laravel-api-response

A Laravel package for consistent, structured API responses. Standardize success and error JSON payloads, reduce repetitive controller code, and keep your API output clean and predictable across your application.

View on GitHub
Deep Wiki
Context7

Getting Started

Install and confirm the version actually resolves

composer require kennedy-osaze/laravel-api-response
composer show kennedy-osaze/laravel-api-response   # note exact version + constraints

The last tagged release is v2.0 (March 2023) and the only documented change is Laravel 10.x support. Before you build anything on it, check the require block in the installed composer.json. If you are on Laravel 11 or 12, expect to either test the resolution manually, add an inline alias constraint, or fork — do not assume the package is maintained against current Laravel.

Read these three files first, in this order

  1. The service provider (src/*ServiceProvider.php) — is it auto-discovered? Does it bind a singleton? Does it register a facade alias or publish config?
  2. The facade / entry class — this is your actual API surface (success, error, and whatever else exists).
  3. The response builder class — this tells you the exact JSON envelope, the default status codes, and whether pagination is detected automatically.

The README is thin, so the source is the documentation. Ten minutes here saves you from guessing the payload shape in a code review.

First use case: standardize one endpoint end-to-end

Pick a single read endpoint, return through the package instead of response()->json(), and hit it with curl to capture the literal envelope:

curl -s -H 'Accept: application/json' https://app.test/api/users/1 | jq

Write that JSON down. That envelope is now a public contract for every mobile client and integration. Get sign-off on it before rolling it out to the other fifty controllers.

// app/Http/Controllers/Api/UserController.php
public function show(User $user)
{
    return ApiResponse::success($user);
}

Implementation Patterns

Wrap at the controller boundary only

Let controllers translate domain results into the package's envelope. Keep Eloquent models, actions, and services ignorant of HTTP. This keeps the "swap the response package later" refactor confined to app/Http.

Create a thin base controller

Rather than repeating status codes and messages, centralize intent:

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

    protected function created(mixed $data)
    {
        return ApiResponse::success($data, 'Created', 201);
    }

    protected function fail(string $message, int $status = 400, array $errors = [])
    {
        return ApiResponse::error($message, $status, $errors);
    }
}

Confirm the argument order against the source — most packages of this shape are (data, message, code) for success and (message, code, errors) for errors, but that is exactly the kind of thing to verify rather than assume.

Route every error path through one place

The package handles the shape; it does not handle exception-to-HTTP mapping. You still need:

  • Validation (422) — override failedValidation() in your FormRequest base class so validation failures return the same envelope as everything else. Otherwise Laravel's default handler emits a differently shaped {message, errors} body and your client's error parser breaks on exactly one endpoint.
  • Auth / authorization (401/403) — AuthorizationException in the exception handler.
  • Model not found (404) — ModelNotFoundException.
  • Unhandled (500) — a catch-all that logs the real exception and returns a generic message.

A single render() / withExceptions() block that funnels all of these through ApiResponse::error(...) is worth more than the package itself.

Pagination — verify the nesting, do not trust it

Laravel paginators already serialize with a data key and meta/links. Passing a paginator straight into a success() helper can produce data.data plus a second meta layer. Check the real output with a 3-row page before committing:

return ApiResponse::success(UserResource::collection($users));

If the shape is wrong, pass the raw arrays instead of the paginator:

return ApiResponse::success(
    UserResource::collection($users)->items(),
    'OK',
    200,
    ['pagination' => [
        'current_page' => $users->currentPage(),
        'per_page'     => $users->perPage(),
        'total'        => $users->total(),
        'last_page'    => $users->lastPage(),
    ]],
);

Whatever you choose, write it once in a base class so all list endpoints match.

API Resources still do the shaping

Use JsonResource / ResourceCollection for field selection and conditional relations, and let the package handle the outer envelope. Don't let resources invent their own {"data": ..., "status": ...} wrapper — that is the package's job.

Lock the contract with tests

Add an assertion helper to your base TestCase and use it everywhere:

protected function assertApiSuccess(TestResponse $response): void
{
    $response->assertJsonStructure(['success', 'message', 'data']);
    $response->assertJsonPath('success', true);
}

Then a single golden-file test (assertExactJson) on one endpoint per resource type catches accidental envelope drift during refactors.

Force JSON on every API request

Add Accept: application/json in your HTTP client / test base URL helper, or attach middleware that sets it. Without it, framework-level 404s and 405s return HTML and your clients hit a parse error instead of a clean error body.

Gotchas and Tips

The maintenance risk is the headline gotcha. One release in the visible history, dated 2023-03, and a description that says nothing about v2.0 beyond the Laravel 10 bump. Pin the exact version in composer.json ("2.0.*"), and treat any future Laravel major upgrade as "this package probably needs a fork." It is a small, self-contained class — forking or vendoring it is a realistic fallback, so keep your usage behind the base controller so that fallback stays cheap.

Check auto-discovery before registering anything manually. If the package predates or sidesteps Laravel's package discovery, you may need the provider in config/app.php and, on Laravel 10, a facade alias in the aliases array. A missing alias produces a confusing "Class 'ApiResponse' not found" that looks like an install failure.

Facade aliases and config:cache. After adding an alias or publishing config, run php artisan config:clear && php artisan config:cache locally and in CI. Alias/provider issues that disappear under config:cache in dev will reappear in production.

Never pass $e->getMessage() into an error payload in production. The convenience of ApiResponse::error($e->getMessage(), 500) is exactly how stack traces and SQL fragments leak. Centralize the 500 path and gate the detail on app()->hasDebugModeEnabled().

Don't mix signalling conventions. Decide now whether clients branch on the HTTP status code or on the success boolean, and be consistent. The classic failure is a 200-with-"success": false from one controller and a 422-with-"success": false from another; clients end up checking both and getting one wrong.

Watch default status codes. Helpers usually default to 200. A store() endpoint returning 200 instead of 201 will pass your tests and quietly break clients that key off status. Be explicit at the call site.

Empty data is a shape bug waiting to happen. success(null) may serialize data: null, while success([]) gives data: []. Pick one per endpoint family and assert it — mobile clients with strict Codable/Jackson models are unforgiving here.

Test both branches of every endpoint. assertOk() alone will not catch an error envelope that changed shape. Cover the 422 and 404 paths on at least one endpoint per controller.

Octane and long-running workers are fine as long as you don't stash per-request state in a singleton or static property on the response builder. Keep usage as a pure factory call per request.

Extension point to know about: if the builder is macroable (common in this style), adding ApiResponse::macro('noContent', ...) is cleaner than a base-controller method for one-off cases. Check the class for Macroable before reaching for a wrapper.

Upgrade checklist for the package itself: branch, bump the constraint, composer update kennedy-osaze/laravel-api-response, run the golden-file envelope test, grep for any direct response()->json( calls that should have been migrated, then deploy behind a canary. Because the surface area is small, a diff of the vendor source between versions is often faster than looking for a changelog that does not exist.

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.
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
cgv/localized-resource