Laravel Custom Wrapper for API Responses

Viewed 3090

I am trying to structure my project in Laravel just created to use it as a back-end API. I want all my responses from Laravel to be returned in the JSON:API format defined on the official site: https://jsonapi.org/format/

For example: I have created the following 2 resource files:

1- php artisan make:resource User

2- php artisan make:resource UserCollection --collection

Two simple resource files, one to return a resource and one to return a collection.

Now, I would like to return in all my responses the following format:

  • In case of success

1- The status return code can be 200, 201 or 202.

2- The returned response should be similar to the following:

{
    "data": [
        {
            "id": 1,
            "email": "collins.elody@example.org"
        }
    ],
    "message": null,
    "success": true
}

You may be wondering what is the point of passing the message key, in this case it is null because it would be returning a collection of records, that is, reading, but in case you needed to add a new record, update or delete one, you would need to pass a message to my front-end, in this case I would use that key for that.

Example, adding record, response status code 201:

{
    "data": [],
    "message": "Record created succesfully",
    "success": true
}
  • In case of failure

As said here: https://jsonapi.org/format/#document-top-level : The members data and errors MUST NOT coexist in the same document.

So, in case of error, I need change data key by errors key, for example, suppose I am trying to authenticate myself, and the validation fails, in this case, it should turn out like this:

{
    "errors": {
        "email": "E-Mail is required",
        "password": "Password is required"
    },
    "message": null,
    "success": false
}

or I just want to return an error message, expected output should by:

{
    "errors": [],
    "message": "Something is Wrong!",
    "success": false
}

So in essence what I need is a global wrapper for all the responses I make from Laravel. I would like to call return in an elegant way as follows:

return $this->success($collection);

or

return $this->success('Done!', 201);

So the first thing that came to mind was creating a trait and defining the methods you need to then call them from anywhere in Laravel

My Trait

<?php

namespace App\Traits;

trait APIResponse
{
    public function success($data, $status = 200) {
        return [
            'data' => $data,
            'success' => in_array($status, [200, 201, 202]) ? true : false,
            'message' => null
        ];
    }

    public function failure($data, $status = 500) {
        // TODO
    }
}

My Controller

class ExampleController extends Controller
{
    public function index() {

        $collection = new UserCollection(User::all());

        return $this->success($collection);
    }
}

But I am not sure it is the right way to do it, please, someone skilled in the field who can help me. Thank you very much in advance.

1 Answers

You are on the right path, there is two main solutions i would consider best approaches, to handling your exact problem. Fractal and Eloquent Resources, i prefer Fractal due to some design decisions and experience.

I will show an example in fractal, using the wrapper by Spatie. Firstly create an serializer that will wrap the data as expected.

class YourCustomSerializer extends SerializerAbstract
{
    public function collection($resourceKey, array $data)
    {
        return [
            $resourceKey ?: 'data' => $data,
            'message': null,
            'success': true,
        ];
    }

    public function item($resourceKey, array $data)
    {
        return [
            $resourceKey ?: 'data' => $data,
            'message': null,
            'success': true,
        ];
    }

This should be added to your fractal.php config, there is published through the spatie wrapper.

Transforming your data you need a transformer.

class UserTransformer extends TransformerAbstract
{
    public function transform(User $user)
    {
        return [
            'name' => $user->name,
            'email' => $user->email,
        ];
    }
}

Now you can transform your data into the expected format.

public function response($data, int $statusCode = Response::HTTP_OK)
{
    return fractal($data, $this->transformer)->respond($statusCode);
}

For error codes, you should go to the Handler.php and add something similar to this. This is very naive way of doing it, but for know should get you going on error handling, you need to do something with validation exception, status code etc.

if ($request->wantsJson()) {
    return response()->json([
        'success' => false,
        'message' => $exception->getMessage(),
    ], Response::HTTP_BAD_REQUEST);
}
Related