Ajeesh
Ajeesh

Reputation: 5860

Consistent REST API Response in Laravel+Dingo

I have been developing a set of rest APIs to be exposed for mobile apps. I am following the repository pattern for the development on the Laravel project. How do I implement a presenter and transformer for formatting a constant JSON output throughout the set of all my APIs?

For example I have the following controller for login

public function authenticate()
    {
        $request = Request::all();  
        try {
                // If authenticated, issue JWT token
                //Showing a dummy response
                return $token;
            }  catch (ValidatorException $e) {
                return Response::json([
                    'error'   =>true,
                    'message' =>$e->getMessageBag()
                ]);
            }
    }

Now where does a transformer and presenter come into the picture? I know that both are used to format the output by converting the db object and produce a formatted JSON so that it remains uniform across my APIs.

The dingo API and fractal or even the framework (L5 repository) don't provide detailed documentation and I can't find any tutorials on this.

I have created the following presenter and transformer for another API which gives the list of products

namespace App\Api\V1\Transformers;

use App\Entities\Product;
use League\Fractal\TransformerAbstract;

class UserTransformer extends TransformerAbstract {

    public function transform(\Product $product)
    {
        return [
            'id'     => (int) $product->products_id
        ];
    }
}

Presenter

<?php

namespace App\Api\V1\Presenters;

use App\Api\V1\Transformers\ProductTransformer;
use Prettus\Repository\Presenter\FractalPresenter;

/**
 * Class ProductPresenter
 *
 * @package namespace App\Presenters;
 */
class ProductPresenter extends FractalPresenter
{
    /**
     * Transformer
     *
     * @return \League\Fractal\TransformerAbstract
     */
    public function getTransformer()
    {
        return new UserTransformer();
    }
}

How will I set the presenter in the controller and respond back? Tried

$this->repository->setPresenter("App\\Presenter\\PostPresenter");

But it doesn't seems to work and the doc doesn't shows the complete steps.

  1. In the above example, how can I make a template for an error response which I can use throughout my APIs and how will I pass my error exceptions to it?
  2. It seems like presenter and transformer can be used to convert database objects into presentable JSON and not anything else. Is that right?
  3. How do you use a presenter and a transformer for a success response and an error response? By passing exceptions, instead of DB objects to the transformer?

Upvotes: 10

Views: 3502

Answers (4)

Tuah Oktorino
Tuah Oktorino

Reputation: 1

You can try use,[Midresapi]: https://github.com/oktorino/midresapi. this will return consistens success or failled response, work in laravel 7 & 8 , Handling validation response, handling 500 response:

$users=\App\User::latest()->limit(2)->get();

return response($users);
#or
return fractal()
        ->collection($users)
        ->transformWith(new \App\Transformers\UserTransformer)
        ->toArray();

Response :

{
"status_code": 200,
"success": true,
"message": "ok",
"data": [
    {
        "username": "dany",
        "email": "[email protected]"
    },
    {
        "username": "scc-client-5150",
        "email": "[email protected]"
    }
  ]
}

Upvotes: 0

tomvo
tomvo

Reputation: 1439

I just now see your similar question here as well. So see my answer on your other question here: https://stackoverflow.com/a/34430595/429719.

From the other question I derived you're using Dingo, so use that as a structured response class. Make sure you're controller extends from Dingo and then you can just return items and collections in a structured way like:

return $this->response->item($user, new UserTransformer);

return $this->response->collection($users, new UserTransformer);

If you want a nice error handling look for the docs here: https://github.com/dingo/api/wiki/Errors-And-Error-Responses

Basically you can throw any of the core exceptions or a few custom Dingo ones. The Dingo layer will catch them and returns a structured JSON response. As per the Dingo docs:

throw new Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException('Nope, no entry today!');

Will generate:

{
    "message": "Nope, no entry today!",
    "status_code": 403
}

Upvotes: 1

Pawel Bieszczad
Pawel Bieszczad

Reputation: 13325

I had the same exact problem and here is how I used dingo with transformer

Controller:

public function update(Request $request)
{

    $bus = new CommandBus([
        $this->commandHandlerMiddleware
    ]);

    $agency = $bus->handle(
        new UpdateAgencyCommand($request->user()->getId(), $request->route('id'), $request->only('name'))
    );

    return $this->response->item($agency, new AgencyTransformer());
}

Transformer:

class AgencyTransformer extends TransformerAbstract
{
    public function transform(AgencyEntity $agencyEntity)
    {
        return [
            'id' => (int) $agencyEntity->getId(),
            'name' => $agencyEntity->getName(),
        ];
    }
}

and this is how I handle errors:

throw new UpdateResourceFailedException('Could not update agency.', $this->agencyUpdateValidator->errors());

Upvotes: 1

tix3
tix3

Reputation: 1152

Fractal is fully documented here: http://fractal.thephpleague.com/ There is an excellent book that I regularly read from the Phil Sturgeon https://leanpub.com/build-apis-you-wont-hate You can find most of the books code available in github https://github.com/philsturgeon/build-apis-you-wont-hate. You can find really nice examples of Fractal in there.

  1. I would create an Api Controller and extend it from my Controllers.In there there should be all the respond functions (respondWithError, respondWithArray etc.)
  2. Tranformers are transforming objects in a consistent json format so that all your endpoints return the same thing for each entity.

  3. Dont really have an answer on this

  4. There are enough examples in Fractal documentation.

Upvotes: -1

Related Questions