json

Using Laravel Resources to build your API

In this article we will learn about Laravel Resources and how to use it in our project.

The Resources classes provide a clean, simple and centralized way to define the structure of the JSON response of each resource of your API. It also works as a transformation layer that sits between your Eloquent models and the JSON responses that are returned to the API clients.

You can easily use the toJson() method (implicitly) to return the model as a JSON object. However, if your API requires further customization, such as modifying the naming conventions for the returned data, or if you need handle relationships between models, you can take this feature to create consistent API resources, customize data output, and use nested resources to handle relationships within the main resource.

Using Resources

For this article we will use an API that provides data about Books as an example, and we have the table and model for books and authors.

So, let’s suppose you have this method in your API

public function getBookById(Request $request){
   return Book::findOrFail($$request->bookId);
}

And you need to expose it in the /book/$bookId path, getting the following json

{
   "id":100,
   "name":"Sample Test Book",
   "created_at":1686679150
}

And maybe we also need to return all attributes with camelCase or wrapped by default in the data field.

To create a Laravel Resource, we will run the following command:

php artisan make:resource BookResource

As a result, you should see now a new file in app/Http/Resources/BookResource.php with the following content:

class BookResource extends JsonResource
{
    public function toArray($request)
    {
        return parent::toArray($request);
    }
}

Now that is the default structure of a Resource, which we can customize based on our requirements. On our case we will define to expose only three attributes, all of them in cameCase.

class BookResource extends JsonResource
{
   public function toArray($request)
   {
       return [
           'id' => $this->id,
           'name' => $this->name,
           'createdAt' => $this->created_at,
       ];
   }

and invoking resource from the action controller

public function getBookById(Request $request){
   return BookResource::make(Book::findOrFail($request->bookId));
}

the resulting JSON will be

{
   "data":{
      "id":100,
      "title":"Sample Test Book",
      "createdAt":1686679150
   }
}

Resources with relationships

Some of the applications that consume the API may require the author of a given book, so the apps can call to /$bookId?expand=author. To achieve that, it will only need add some extra logic to check the query param on the resource and also define the AuthorResource

class BookResource extends JsonResource
{

    public function toArray($request)
    {
        $response = [
            'id'    => $this->rrk_book_id,
            'title' => $this->title
            'createdAt' => $this->created_at
        ];

        if ($this->shouldExpandAuthor($request)) {
            $response['relations']['author'] = AuthorResource::make($this->resource->author);
        }

        return $response;
    }

    public function shouldExpandAuthor($request)
    {
        return str_contains($request->query('expand'), 'author');
    }

}

Resource collections

And what happens if you need an endpoint to return a list of books?

For that you can define the collection with the command

php artisan make:resource BookCollection

As a result, you should see now a new file in app/Http/Resources/BookCollection.php and you need specify the $collect with your resource, in this case

public $collects = BookResource::class;
class BookCollection extends ResourceCollection
{
    
    public $collects = BookResource::class;

    public function toArray($request)
    {
        return $this->collection;
    }
}

then you need invoking the resource collection from the action controller with BookCollection::make(Book::all())

the resulting JSON will be

{
   "data":[
      {
         "id":100,
         "title":"Sample Test Book",
         "createdAt":1686679150
      },
      {
         "id":101,
         "title":"Sample Test Book 2",
         "createdAt":1686679150
      },
      {
         "id":102,
         "title":"Mike",
         "createdAt":1686679150
      }
   ]
}

If you need to return a collection of books with the relation author you just need to request it.
Also if in addition to the previous need you want to paginate we invoke the pagination and that will include the meta and links for that.

So depending on the case maybe you need to define only one resource for each eloquent model (when it’s needed) or define a couple of resources to deal different things (you can inherit from the parent resource) to reuse depending on the case.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.