close

DEV Community

Oz Uzair
Oz Uzair

Posted on

Stop rewriting your API responses in Laravel (Use this Trait instead)

If you are building API-driven applications, nothing clutters up your controllers faster than manually typing out response()->json(...) arrays every single time you need to return data or throw an error.

When you have inconsistent response structures, your frontend (and the developers consuming your API) will constantly have to guess whether the data is nested under ['data'], ['payload'], or just at the root of the object.

The cleanest way I've found to standardize this across an entire application is by creating a dedicated ApiResponse trait.

Instead of rewriting your JSON structure in every controller method, create this trait in your app/Traits directory:

namespace App\Traits;

use Illuminate\Http\JsonResponse;

trait ApiResponse 
{
    protected function success(mixed $data, ?string $message = null, int $code = 200): JsonResponse 
    {
        return response()->json([
            'status' => 'success',
            'message' => $message,
            'data' => $data
        ], $code);
    }

    protected function error(string $message, int $code = 400, array|string $errors = []): JsonResponse 
    {
        // Force errors into an array format for consistent frontend parsing
        $formattedErrors = is_string($errors) ? [$errors] : $errors;

        return response()->json([
            'status' => 'error',
            'message' => $message,
            'errors' => $formattedErrors
        ], $code);
    }
}
Enter fullscreen mode Exit fullscreen mode

Next, simply use this trait inside your base Controller.php.

Now, your actual endpoints become incredibly readable and strictly standardized:

namespace App\Http\Controllers;

use App\Models\Task;
use Illuminate\Http\Request;
use Illuminate\Http\JsonResponse;
use Throwable;

class TaskController extends Controller
{
    public function store(Request $request): JsonResponse
    {
        $validated = $request->validate([
            'title' => 'required|string|max:255',
            'description' => 'nullable|string'
        ]);

        try {
            $task = Task::create($validated);

            return $this->success($task, 'Task successfully generated', 201);

        } catch (Throwable $e) {
            // Note: Exposing raw exception messages is fine for local dev, 
            // but should be sanitized or logged in production environments.
            return $this->error('Failed to generate task', 500, [$e->getMessage()]);
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

This guarantees that every single endpoint in your application returns the exact same JSON signature. Your frontend will thank you.

How are you handling global API responses in your current stack? Do you use traits, or do you prefer wrapping everything in dedicated Resource classes? Let me know below.

Disclosure: Formatted using Ai so I don't get laughed at with grammatical mistakes 😂

Top comments (0)