Introduction
Imagine working with an application that each endpoint returns the data for successful and error calls in a different way. If it's a small and simple app maintained by a single person it can be a little easier, but if the application starts to grow and more people start joining the team, in a small window of time it will be total chaos to maintain and improve the application.
Having a standardised structure for the responses of your application can help a lot in improving its quality and maintainability of it. In this article, we will learn a simple way to do this using Laravel Responsable classes.
What's Laravel Responsable Classes?
Laravel has an interface: Illuminate\Contracts\Support\Responsable
that can be used to create classes that
can be converted into HTTP Responses. This is a really simple interface that has only one method:
interface Responsable
{
/**
* Create an HTTP response that represents the object.
*
* @param \Illuminate\Http\Request $request
* @return \Symfony\Component\HttpFoundation\Response
*/
public function toResponse($request);
}
Creating Custom Response Classes
Creating custom Response classes gives us a lot of power, but we need to be careful about how to use it. I already saw many codebases and many articles explaining how to use custom Response classes to format data and apply some logic in the data before sending it, but personally, I don't think that's a good way of using this feature. For me, code formatting and logic should be in another layer of the application.
But one amazing thing we can do with this feature is to create standardised responses for our applications. Having this in our applications can highly improve their quality and maintainability because you already know the structure to expect from your application.
In the sections below we will create two different custom response classes, one for successful API responses and another one for failed API responses.
Creating the API Success Response
Imagine that in your application your endpoints always return the data needed for it, but it also can return
some metadata that can contain a message or notification to show to the user, pagination info,
etc. With this in mind, we can create a simple custom Response class that will always return two
properties in your responses: data
and metadata
. The custom Response class would be something like this:
<?php
namespace App\Http\Responses;
use Illuminate\Contracts\Support\Responsable;
use Illuminate\Http\Response;
class ApiSuccessResponse implements Responsable
{
/**
* @param mixed $data
* @param array $metadata
* @param int $code
* @param array $headers
*/
public function __construct(
private mixed $data,
private array $metadata,
private int $code = Response::HTTP_OK,
private array $headers = []
) {}
/**
* @param $request
* @return \Symfony\Component\HttpFoundation\Response|void
*/
public function toResponse($request)
{
return response()->json(
[
'data' => $this->data,
'metadata' => $this->metadata,
],
$this->code,
$this->headers
);
}
}
With this class, we can ensure that:
- The response will always be in JSON format.
- We will always return the
data
andmetadata
properties. - We can customize the HTTP Code and headers if needed.
Now you can use this response for your endpoints:
class UserController extends Controller
{
public function store(CreateUserRequest $request): JsonResponse
{
$user = $this->service->create($request->all());
return new ApiSuccessResponse(
$user,
['message' => 'User was created successfully'],
Response::HTTP_CREATED
);
}
}
Creating the API Error Response
Imagine that in your application you want to always return an error message when something goes wrong, but
you want to also add some debug information if the debug is set to true
. With this in mind, we can
create another simple custom Response class that will do that:
<?php
namespace App\Http\Responses;
use Illuminate\Contracts\Support\Responsable;
use Illuminate\Http\Response;
use Throwable;
class ApiErrorResponse implements Responsable
{
public function __construct(
private string $message,
private ?Throwable $exception = null,
private int $code = Response::HTTP_INTERNAL_SERVER_ERROR,
private array $headers = []
) {}
/**
* @param $request
* @return \Symfony\Component\HttpFoundation\Response|void
*/
public function toResponse($request)
{
$response = ['message' => $this->message];
if (! is_null($this->exception) && config('app.debug')) {
$response['debug'] = [
'message' => $this->exception->getMessage(),
'file' => $this->exception->getFile(),
'line' => $this->exception->getLine(),
'trace' => $this->exception->getTraceAsString()
];
}
return response()->json($response, $this->code, $this->headers);
}
}
With this class, we can ensure that:
- The response will always be in JSON format.
- We will always return the
message
property. - The debug info will be added if needed.
- We can customize the HTTP Code and headers if needed.
Now you can use this response for your endpoints:
class UserController extends Controller
{
public function store(CreateUserRequest $request): JsonResponse
{
try {
$user = $this->service->create($request->all());
return new ApiSuccessResponse(
$user,
['message' => 'User was created successfully'],
Response::HTTP_CREATED
);
} catch (Throwable $exception) {
return new ApiErrorResponse(
'An error occurred while trying to create the user',
$exception
);
}
}
}
Conclusion
In this article, we learned how to use Laravel Responsable classes to create standardised API responses for our applications to improve their quality and maintainability. The examples in this article were just to show how to use this feature, but you can customize the custom Response classes for the needs of your projects.
I hope that you liked this article and if you do, don’t forget to share this article with your friends!!! See ya!