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 Json Response Laravel Package

kevupton/laravel-json-response

A small Laravel package for returning consistent JSON responses from controllers. It offers helpers for building structured success and error payloads, helping keep API output uniform across your application.

View on GitHub
Deep Wiki
Context7

Getting Started

Install and verify compatibility first. This is a small, low-traffic package, so treat the install as an audit rather than a one-liner:

composer require kevupton/laravel-json-response

Before writing any code, open vendor/kevupton/laravel-json-response/composer.json and check the illuminate/* constraints against your app's Laravel version. If the constraint is pinned to an older major (e.g. 5.4.*), you have three options in order of preference: (1) install with --ignore-platform-reqs and run the test suite to see if it actually works, (2) fork it into your own namespace and widen the constraints, or (3) skip the dependency and copy the 30–60 lines of response formatting into a trait in app/Http/Responses/. For a package this small, option 3 is often cheaper than maintaining a fork.

Where to look first. Read the package source before the README — the whole thing is likely one service provider plus one trait/class:

  • src/*ServiceProvider.php — shows whether it registers a config file, a facade, response macros, or just auto-loads a trait.
  • The main response class/trait — this is your actual API surface (respondSuccess, respondError, json, etc.). Note the exact method names and signatures; the README may be stale.
  • config/*.php if present — usually holds default HTTP codes, envelope keys, and message strings.

Register/publish what's needed. If a config file exists:

php artisan vendor:publish --provider="Kevupton\JsonResponse\JsonResponseServiceProvider"

Then php artisan config:cache and confirm the config key appears in config/ (or bootstrap/cache/config.php).

First use case: standardize one endpoint. Do not retrofit the whole API. Pick a single controller, apply the package's trait (or return its response object), and lock the shape down with a test:

public function show($id)
{
    return $this->respondSuccess(UserResource::make(User::findOrFail($id)));
}
$response = $this->getJson("/api/users/{$user->id}");

$response->assertOk()
         ->assertHeader('Content-Type', 'application/json')
         ->assertJsonStructure(['status', 'message', 'data' => ['id', 'name']]);

Once that test is green, you know exactly what the envelope looks like and can roll it out deliberately.

Implementation Patterns

Trait-on-a-base-controller. The highest-leverage pattern: apply the response trait once on an abstract ApiController, have every resource controller extend it, and never touch the trait again. This gives you a single place to swap the envelope later without editing dozens of controllers.

abstract class ApiController extends Controller
{
    use RespondsWithJson; // name per package source
}

class UserController extends ApiController { /* ... */ }

Wrap JsonResource / paginated collections, don't replace them. Let Laravel do the serialization and let the package do the envelope. Pass the resource into the data slot rather than hand-building arrays — you keep whenLoaded, whenCounted, and conditional attributes working.

return $this->respondSuccess(UserResource::collection($users));

One caveat: paginated resources carry links and meta inside their own payload. If the package buries everything under data, clients lose the top-level pagination contract — verify the output and hoist pagination into a sibling meta key if needed.

Normalize errors in the exception handler, not in every controller. The real payoff of a response package is consistent error bodies. Map the common exceptions once in app/Exceptions/Handler.php:

public function render($request, Throwable $e)
{
    if ($request->expectsJson()) {
        if ($e instanceof ValidationException) {
            return $this->respondError($e->errors(), 422);
        }
        if ($e instanceof ModelNotFoundException) {
            return $this->respondError('Not found', 404);
        }
    }

    return parent::render($request, $e);
}

This is where the package earns its keep — status codes, messages, and the envelope stay in one file instead of drifting per-controller.

Use it for non-controller responses too. FormRequest failedValidation, middleware short-circuits (abort replacements), and queued-job API callbacks all benefit from returning the same shape. If the package registers a Response facade or macro, you can call it from anywhere; otherwise inject/instantiate the response class.

Test the contract, not the implementation. Add a single assertJsonStructure helper or a Pest expectation that every API test reuses. That way, if you ever swap this package for laravel-fractal, spatie/laravel-responsecache-style envelopes, or a hand-rolled trait, only one assertion needs to change and you find every endpoint that drifted.

Integration tip: if your frontend SDK or mobile client parses {status, message, data}, define that shape in one place (an OpenAPI snippet or a TS type) and generate it from the same test that asserts the structure. This package is thin enough that the client contract — not the package — is the thing worth defending.

Gotchas and Tips

Maintenance risk is the primary gotcha. Sixteen stars and a low score mean the package is unlikely to receive Laravel 10/11 compatibility updates. Pin the version in composer.json, and add a note in your README pointing at the exact class you depend on, so a future upgrade path (fork or inline the trait) is obvious. Set a calendar reminder to re-evaluate on each Laravel major bump.

HTTP status vs. body status. Many small response packages set a status field in the JSON body but forget the actual HTTP status code, or vice versa. Assert both in tests: ->assertStatus(201) and ->assertJsonPath('status', 201). If they diverge, wrap the package's helper so your codebase has one correct implementation.

Content-Type drift. Returning a plain PHP array from a controller yields JSON, but returning the package's object after a non-JSON middleware or a misconfigured Accept header can produce text/html. Always assert the header explicitly; mobile clients and axios behave differently when the content type is wrong.

Message leakage in production. If the package accepts an exception or a raw message and passes it through, APP_DEBUG=false won't save you — the package doesn't know about your environment. Sanitize in the exception handler: return a generic message to clients, log the real one.

Config caching bites twice. Publish → edit config → forget config:cache is the classic loop. If a change to config/json-response.php appears to do nothing, run php artisan config:clear before debugging anything else.

Debugging output. dd($response->getContent()) on the response object is faster than reading logs. For the "why is my data empty" case, check whether the package expects an array and you passed a Collection or JsonResource — many hand-rolled helpers do array_merge or count() and silently mangle objects. Cast or wrap explicitly.

Extension points, in order of preference:

  1. Config — if the envelope keys are configurable, use that rather than subclassing.
  2. Subclass the package's response class and bind yours in a service provider ($this->app->bind(JsonResponse::class, AppJsonResponse::class)) so overrides don't touch vendor code.
  3. Fork only when the package is genuinely abandoned and you need Laravel-version compatibility — at which point, seriously consider inlining the trait instead.

Don't fight the envelope for special cases. File downloads, streamed responses, and health-check endpoints should bypass the package entirely. Mixing enveloped JSON and raw responses in the same controller is a smell — split the route or return response()->json() directly for the exception, and document why.

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.
socialdept/atp-orm
cboxdk/laravel-assistant-client
socialdept/atp-resolver
socialdept/tenant-domains
tautvydasr/blade-class-names
socialdept/color-scales
maherelgamil/controller
coleus/widgets
coleus/table
coleus/users
coleus/music
coleus/health
coleus/calendar
coleus/notes
coleus/support
coleus/settings
coleus/apps
seampact/laravel
emmyconceptng/laravel-ng-postcode
jjsquady/round-robin