{"id":1190,"date":"2017-09-21T14:02:33","date_gmt":"2017-09-21T21:02:33","guid":{"rendered":"http:\/\/mitchmckenna.com\/blog\/?p=1190"},"modified":"2017-09-21T14:44:18","modified_gmt":"2017-09-21T21:44:18","slug":"laravel-api-resources-json-api-spec","status":"publish","type":"post","link":"http:\/\/mitchmckenna.com\/blog\/2017\/09\/laravel-api-resources-json-api-spec\/","title":{"rendered":"Laravel API Resources + JSON API Spec"},"content":{"rendered":"<p><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" data-attachment-id=\"1241\" data-permalink=\"http:\/\/mitchmckenna.com\/blog\/2017\/09\/laravel-api-resources-json-api-spec\/laravel-api-resource-json-api-logos\/\" data-orig-file=\"https:\/\/i0.wp.com\/mitchmckenna.com\/blog\/wp-content\/uploads\/2017\/09\/laravel-api-resource-json-api-logos.png?fit=600%2C220\" data-orig-size=\"600,220\" data-comments-opened=\"1\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}\" data-image-title=\"laravel-api-resource-json-api-logos\" data-image-description=\"\" data-image-caption=\"\" data-medium-file=\"https:\/\/i0.wp.com\/mitchmckenna.com\/blog\/wp-content\/uploads\/2017\/09\/laravel-api-resource-json-api-logos.png?fit=300%2C110\" data-large-file=\"https:\/\/i0.wp.com\/mitchmckenna.com\/blog\/wp-content\/uploads\/2017\/09\/laravel-api-resource-json-api-logos.png?fit=600%2C220\" class=\"aligncenter size-full wp-image-1241\" src=\"https:\/\/i0.wp.com\/mitchmckenna.com\/blog\/wp-content\/uploads\/2017\/09\/laravel-api-resource-json-api-logos.png?resize=600%2C220\" alt=\"\" width=\"600\" height=\"220\" \/><\/p>\n<p>Laravel 5.5 launched with API Resources, a new feature for building APIs. API Resources serve as a way to transform models and collections to JSON formatted API responses. It standardizes how to define the output format of models and includes helper methods for common tasks like customizing metadata, headers and pagination. You can read how to write API Resources in the <a href=\"https:\/\/laravel.com\/docs\/5.5\/eloquent-resources\">Laravel docs<\/a>.<\/p>\n<p><a href=\"http:\/\/jsonapi.org\/\">JSON API<\/a> is a spec created from common conventions for building JSON powered APIs. The goal of JSON API spec is to help teams avoid wasting time debating API structure and features. JSON API encourages RESTful design, readability\/<wbr>discoverability, and object relationships through a flat document structure. There\u2019s examples for almost every feature on the <a href=\"http:\/\/jsonapi.org\/format\/\">Specification<\/a> page, and you can learn a lot about why JSON API is designed the way that it is by reading through the <a href=\"http:\/\/jsonapi.org\/faq\/#why-not-use-the-hal-specification\">FAQ<\/a> page.<\/p>\n<p>JSON API was <a href=\"https:\/\/github.com\/laravel\/framework\/pull\/19449#issuecomment-308717548\">considered for the default format<\/a> of API Resources, however in the end it was decided to be too much work for the average use-case of a Laravel-powered API. For this reason, API Resources don\u2019t automatically output to JSON API format &#8211; but it is very similar, even including the same root offsets. In this article we\u2019ll take a look at what it takes to make API Resources fully JSON API compliant.<\/p>\n<h2>Making API Resources JSON API Compliant<\/h2>\n<p>API Resources use the following same root offsets as JSON API: <code>data<\/code>, <code>meta<\/code> and <code>links<\/code>.<\/p>\n<ul>\n<li><code>data<\/code> \u2013 the primary resource (or collection of resources)<\/li>\n<li><code>meta<\/code> \u2013 additional information about a resource that is not an attribute or relationship<\/li>\n<li><code>links<\/code> linkage eg. \u201cself\u201d link or pagination urls<\/li>\n<\/ul>\n<p>With Laravel&#8217;s API Resources, you can generate two types of classes: resources and resource collections. A resource class is for outputting a single model you pass to it, and a resource collection is for multiple models (eg. a paginated list of items). A resource, without customizing it, will just call <code>toArray()<\/code> on the model and place the result under the <code>data<\/code> offset. A resource collection will do the same for each\u00a0model, and add them as an array to the <code>data<\/code> offset. A resource collection will also automagically add <a href=\"https:\/\/laravel.com\/docs\/5.5\/eloquent-resources#pagination\">pagination<\/a> information to the <code>meta<\/code> and <code>links<\/code> offsets.<\/p>\n<p>API Resources don\u2019t however include automation for JSON API\u2019s <a href=\"http:\/\/jsonapi.org\/format\/#document-compound-documents\">compound documents<\/a> and their <code>relationships<\/code> \/ <code>included<\/code> offsets; a requirement in JSON API. Let\u2019s take a look at at how we can write a resource and a resource collection to comply with the JSON API spec. We\u2019ll use a sales platform concept for examples where we have orders of products, and for simplicity we\u2019ll say an order can only be for one or more of a single product.<\/p>\n<h3>Resources<\/h3>\n<p>First let\u2019s take a look at an endpoint which returns a single resource (eg. <code>\/orders\/1<\/code>). In this example we have an <code>Order<\/code> model, and each <code>Order<\/code> has a one-to-one relationship with a <code>Product<\/code>. By default when a model is passed to a resource class like <code>OrderResource<\/code>, it outputs a response in the structure of the one on the left in the table below. Compare this to the equivalent response in JSON API format on the right:<\/p>\n<table>\n<tbody>\n<tr>\n<td>API Resource (Default)<\/td>\n<td>JSON API Spec (End Goal)<\/td>\n<\/tr>\n<tr>\n<td style=\"width: 50%;\">\n<pre style=\"margin-bottom: 0px;\"><code style=\"font-size: 10px;\">{\r\n  \"data\": {\r\n    \"id\": 1,\r\n    \"order_id\": 246752514,\r\n    \"product_id\": 2,\r\n    \"quantity\": 9,\r\n    \"created_at\": \"2009-12-22 21:40:02\",\r\n    \"updated_at\": \"2017-08-18 22:07:53\",\r\n    \"product\": {\r\n      \"id\": 2,\r\n      \"name\": \"Nike Shoes\",\r\n      \"created_at\": \"2017-08-17 19:49:32\",\r\n      \"updated_at\": \"2017-08-17 19:49:32\"\r\n    }\r\n  }\r\n}<\/code><\/pre>\n<\/td>\n<td style=\"width: 50%;\">\n<pre style=\"margin-bottom: 0px;\"><code style=\"font-size: 10px;\">{\r\n \u00a0\"data\": {\r\n \u00a0\u00a0\u00a0\"type\": \"order\",\r\n \u00a0\u00a0\u00a0\"id\": \"1\",\r\n \u00a0\u00a0\u00a0\"attributes\": {\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\"order_id\": 246752514,\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\"quantity\": 9,\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\"created_at\": 1261518002,\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\"updated_at\": 1503094073\r\n \u00a0\u00a0\u00a0},\r\n \u00a0\u00a0\u00a0\"relationships\": {\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\"product\": {\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\"data\": {\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\"type\": \"product\",\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\"id\": \"2\"\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0}\r\n \u00a0\u00a0\u00a0\u00a0\u00a0}\r\n \u00a0\u00a0\u00a0}\r\n \u00a0},\r\n \u00a0\"included\": [\r\n \u00a0\u00a0\u00a0{\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\"type\": \"product\",\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\"id\": \"2\",\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\"attributes\": {\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\"name\": \"Nike Shoes\",\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\"created_at\": 1502999372,\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\"updated_at\": 1502999372\r\n \u00a0\u00a0\u00a0\u00a0\u00a0}\r\n \u00a0\u00a0\u00a0}\r\n \u00a0]\r\n}<\/code><\/pre>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<blockquote><p>\nNote: If you are new to JSON API, it may appear that JSON API\u2019s version of the output is at a disadvantage in response size, but keep in mind the product may not be <a href=\"http:\/\/jsonapi.org\/format\/#fetching-includes\">included<\/a> by default. In addition, JSON API is providing added clarity that the related product\u2019s data will not be able to be PATCH\u2019ed on this endpoint; it should instead be updated via it\u2019s own resource\u2019s endpoint (eg. <code>PATCH \/products\/6<\/code>). You could add a <a href=\"http:\/\/jsonapi.org\/format\/#document-links\"><code>links<\/code><\/a> offset to the product object with a <code>self<\/code> url to further encourage <a href=\"http:\/\/jsonapi.org\/recommendations\/#including-links\">discoverability<\/a>.\n<\/p><\/blockquote>\n<h6>How to write <code>Resource<\/code> classes to output to JSON API format<\/h6>\n<p>After <a href=\"https:\/\/laravel.com\/docs\/5.5\/eloquent-resources#generating-resources\">creating the resource<\/a> class, update the <code>toArray()<\/code> function to serialize the model in the correct format. This includes setting the <code>type<\/code> and <code>id<\/code> fields required by JSON API, and putting the rest of the resource\u2019s data under <code>attributes<\/code>. \u00a0You can also add a <code>with()<\/code>function to include any additional root offsets. We&#8217;ll use <code>with()<\/code> to add the related <code>Product<\/code> resource in the <code>included<\/code> offset. Here\u2019s an example of what the <code>OrderResource<\/code> class would look like:<\/p>\n<pre><code>class OrderResource extends Resource\r\n{\r\n \u00a0\u00a0public function toArray($request)\r\n \u00a0\u00a0{\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0return [\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0'type' =&gt; 'order',\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0'id' =&gt; (string) $this-&gt;id,\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0'attributes' =&gt; [\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0'name' =&gt; $this-&gt;name,\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0'created_at' =&gt; $this-&gt;created_at,\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0'updated_at' =&gt; $this-&gt;updated_at,\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0],\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0'relationships' =&gt; [\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0'product' =&gt; [\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0'data' =&gt; [\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0'type' =&gt; 'product',\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0'id' =&gt; (string) $this-&gt;product_id\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0]\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0]\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0]\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0];\r\n \u00a0\u00a0\u00a0}\r\n\r\n \u00a0\u00a0\u00a0public function with($request)\r\n \u00a0\u00a0\u00a0{\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0return ['included' =&gt; [new ProductResource($this-&gt;product)]];\r\n \u00a0\u00a0\u00a0}\r\n}\r\n<\/code><\/pre>\n<p>Now when you use the <code>OrderResource<\/code> you\u2019ll automatically get JSON API compliant output.<\/p>\n<h3>Resource Collections<\/h3>\n<p>Now let\u2019s take a look at endpoints that return a collection of resources (eg. <code>\/orders?page=2<\/code>). On the left we have the the default output from a <code>ResourceCollection<\/code> class <code>OrderCollection<\/code>, and on the right what the equivalent would look like following JSON API format:<\/p>\n<table>\n<tbody>\n<tr>\n<td>ResourceCollection (Default)<\/td>\n<td>JSON API Spec (End Goal)<\/td>\n<\/tr>\n<tr>\n<td style=\"width: 50%;\">\n<pre style=\"margin-bottom: 0px;\"><code style=\"font-size: 10px;\">{\r\n \u00a0\"data\": [\r\n \u00a0\u00a0\u00a0{\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\"id\": 18,\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\"order_id\": 344501705,\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\"product_id\": 6,\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\"quantity\": 2,\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\"created_at\": \"2010-01-22 03:36:03\",\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\"updated_at\": \"2015-07-23 12:19:02\",\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\"product\": {\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\"id\": 6,\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\"name\": \"Tommy Jersey\",\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\"created_at\": \"2017-08-17 19:49:32\",\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\"updated_at\": \"2017-08-17 19:49:32\"\r\n \u00a0\u00a0\u00a0\u00a0\u00a0}\r\n \u00a0\u00a0\u00a0},\r\n \u00a0\u00a0\u00a0{\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\"id\": 19,\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\"order_id\": 446228260,\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\"product_id\": 6,\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\"quantity\": 2,\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\"created_at\": \"2010-01-22 07:23:13\",\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\"updated_at\": \"2010-01-22 07:25:52\",\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\"product\": {\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\"id\": 6,\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\"name\": \"Tommy Jersey\",\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\"created_at\": \"2017-08-17 19:49:32\",\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\"updated_at\": \"2017-08-17 19:49:32\"\r\n \u00a0\u00a0\u00a0\u00a0\u00a0}\r\n \u00a0\u00a0\u00a0}\r\n \u00a0],\r\n \u00a0\"links\": {\r\n \u00a0\u00a0\u00a0\"first\": \"http:\/\/api.dev\/orders?page=1\",\r\n \u00a0\u00a0\u00a0\"last\": \"http:\/\/api.dev\/orders?page=330\",\r\n \u00a0\u00a0\u00a0\"prev\": \"http:\/\/api.dev\/orders?page=6\",\r\n \u00a0\u00a0\u00a0\"next\": \"http:\/\/api.dev\/orders?page=8\"\r\n \u00a0},\r\n \u00a0\"meta\": {\r\n \u00a0\u00a0\u00a0\"current_page\": 7,\r\n \u00a0\u00a0\u00a0\"from\": 13,\r\n \u00a0\u00a0\u00a0\"last_page\": 330,\r\n \u00a0\u00a0\u00a0\"path\": \"http:\/\/api.dev\/orders\",\r\n \u00a0\u00a0\u00a0\"per_page\": \"2\",\r\n \u00a0\u00a0\u00a0\"to\": 14,\r\n \u00a0\u00a0\u00a0\"total\": 659\r\n \u00a0}\r\n}\r\n<\/code><\/pre>\n<\/td>\n<td style=\"width: 50%;\">\n<pre style=\"margin-bottom: 0px;\"><code style=\"font-size: 10px;\">{\r\n  \"data\": [\r\n    {\r\n      \"type\": \"order\",\r\n      \"id\": \"18\",\r\n      \"attributes\": {\r\n        \"order_id\": 344501705,\r\n        \"quantity\": 2,\r\n        \"created_at\": 1264131363,\r\n        \"updated_at\": 1437653942\r\n      },\r\n      \"relationships\": {\r\n        \"product\": {\r\n          \"data\": {\r\n            \"type\": \"product\",\r\n            \"id\": \"6\"\r\n          }\r\n        }\r\n      }\r\n    },\r\n    {\r\n      \"type\": \"order\",\r\n      \"id\": \"19\",\r\n      \"attributes\": {\r\n        \"order_id\": 446228260,\r\n        \"quantity\": 2,\r\n        \"created_at\": 1264144993,\r\n        \"updated_at\": 1264145152\r\n      },\r\n      \"relationships\": {\r\n        \"product\": {\r\n          \"data\": {\r\n            \"type\": \"product\",\r\n            \"id\": \"6\"\r\n          }\r\n        }\r\n      }\r\n    }\r\n  ],\r\n  \"links\": {\r\n    \"first\": \"http:\/\/api.dev\/orders?page=1\",\r\n    \"last\": \"http:\/\/api.dev\/orders?page=330\",\r\n    \"prev\": \"http:\/\/api.dev\/orders?page=6\",\r\n    \"next\": \"http:\/\/api.dev\/orders?page=8\"\r\n  },\r\n  \"meta\": {\r\n    \"current_page\": 7,\r\n    \"from\": 13,\r\n    \"last_page\": 330,\r\n    \"path\": \"http:\/\/api.dev\/orders\",\r\n    \"per_page\": \"2\",\r\n    \"to\": 14,\r\n    \"total\": 659\r\n  },\r\n  \"included\": [\r\n    {\r\n      \"type\": \"product\",\r\n      \"id\": \"6\",\r\n      \"attributes\": {\r\n        \"name\": \"Tommy Jersey\",\r\n        \"created_at\": 1502999372,\r\n        \"updated_at\": 1502999372\r\n      }\r\n    }\r\n  ]\r\n}<\/code><\/pre>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<h6>How to write <code>ResourceCollection<\/code> classes in JSON API format<\/h6>\n<p>For endpoints that return a collection of resources, we can reuse the individual <code>Resource<\/code> classes like the one we wrote in the previous section and map each model to it. For including related resources, keep in mind the benefit of having them under the <code>included<\/code> offset instead of embedding them recursively is so the data isn\u2019t duplicated over and over. For example, in the output above the product \u201cTommy Jersey\u201d is duplicated in each order. To make sure you don\u2019t have duplicates in the <code>included<\/code> offset, you can use the <code>unique()<\/code> method on collections. Here\u2019s an example of what the <code>OrderCollection<\/code> class would look like:<\/p>\n<pre><code>class OrderCollection\r\n{\r\n \u00a0\u00a0public function toArray($request)\r\n \u00a0\u00a0{\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0return [\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0'data' =&gt; $this-&gt;collection-&gt;map(function ($order) use ($request) {\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0return (new OrderResource($order))-&gt;toArray($request);\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0})\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0];\r\n \u00a0\u00a0}\r\n\r\n\r\n \u00a0\u00a0public function with($request)\r\n \u00a0\u00a0{\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0return [\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0'included' =&gt; $this-&gt;collection-&gt;pluck('product')-&gt;unique()-&gt;values()-&gt;map(function ($product) {\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0return new ProductResource($product);\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0})\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0];\r\n \u00a0\u00a0}\r\n}<\/code><\/pre>\n<p>At this point we have responses for both individual resources and their collections which comply with the JSON API spec! You can find a fully working version of the code above in the <code>feature\/api-resources<\/code> branch of an example API codebase <a href=\"https:\/\/github.com\/MitchellMcKenna\/product-sales-api\/tree\/feature\/api-resources\">here<\/a>.<\/p>\n<h2>What JSON API Features Are Missing?<\/h2>\n<p>With these changes, you now have an API that outputs in valid JSON API format, but API Resources don\u2019t automatically support some of the more advanced (optional) features such as giving clients control over <a href=\"http:\/\/jsonapi.org\/format\/#fetching-includes\">inclusion of related resources<\/a> using the \u201cinclude\u201d query param, or the ability for clients to reduce payload size by specifying to return only the fields they need using <a href=\"http:\/\/jsonapi.org\/format\/#fetching-sparse-fieldsets\">sparse fieldsets<\/a>.<\/p>\n<p>Prior to the release of API Resources in Laravel 5.5, <a href=\"http:\/\/fractal.thephpleague.com\/\">Fractal<\/a> was a popular PHP package which offered an alternative implementation of a transformation layer in APIs. It has support for outputting to JSON API format built-in by default. As well, Fractal includes the more advanced features we mentioned above like the \u201cinclude\u201d query param, and sparse fieldsets. In my next blog post I\u2019ll explore if you can swap out Fractal for native Laravel API Resources, AND if you should.<\/p>\n<p><a href=\"http:\/\/mitchmckenna.com\/blog\/category\/programming\/feed\/\">Subscribe<\/a> or <a href=\"https:\/\/twitter.com\/mitchellmckenna\">follow me<\/a> on twitter so you don\u2019t miss the next one!<\/p>\n","protected":false},"excerpt":{"rendered":"<a href=\"http:\/\/mitchmckenna.com\/blog\/2017\/09\/laravel-api-resources-json-api-spec\/\" rel=\"bookmark\" title=\"Permalink to Laravel API Resources + JSON API Spec\"><p>API Resources are a new feature in Laravel 5.5 to convert objects to JSON responses. Here&#8217;s how to write API Resources that follow JSON API spec  [\u2026]<\/p>\n<\/a>","protected":false},"author":1,"featured_media":1230,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[171,16],"tags":[174,172,175],"class_list":{"0":"post-1190","1":"post","2":"type-post","3":"status-publish","4":"format-standard","5":"has-post-thumbnail","7":"category-laravel","8":"category-programming","9":"tag-jsonapi","10":"tag-laravel","11":"tag-restapi","12":"h-entry","13":"hentry"},"jetpack_featured_media_url":"https:\/\/i0.wp.com\/mitchmckenna.com\/blog\/wp-content\/uploads\/2017\/09\/laravel-api-resource-json-api.png?fit=700%2C350","jetpack_shortlink":"https:\/\/wp.me\/p92wZA-jc","jetpack-related-posts":[],"jetpack_sharing_enabled":true,"_links":{"self":[{"href":"http:\/\/mitchmckenna.com\/blog\/wp-json\/wp\/v2\/posts\/1190","targetHints":{"allow":["GET"]}}],"collection":[{"href":"http:\/\/mitchmckenna.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/mitchmckenna.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/mitchmckenna.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"http:\/\/mitchmckenna.com\/blog\/wp-json\/wp\/v2\/comments?post=1190"}],"version-history":[{"count":163,"href":"http:\/\/mitchmckenna.com\/blog\/wp-json\/wp\/v2\/posts\/1190\/revisions"}],"predecessor-version":[{"id":1364,"href":"http:\/\/mitchmckenna.com\/blog\/wp-json\/wp\/v2\/posts\/1190\/revisions\/1364"}],"wp:featuredmedia":[{"embeddable":true,"href":"http:\/\/mitchmckenna.com\/blog\/wp-json\/wp\/v2\/media\/1230"}],"wp:attachment":[{"href":"http:\/\/mitchmckenna.com\/blog\/wp-json\/wp\/v2\/media?parent=1190"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/mitchmckenna.com\/blog\/wp-json\/wp\/v2\/categories?post=1190"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/mitchmckenna.com\/blog\/wp-json\/wp\/v2\/tags?post=1190"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}