Building APIs in Laravel has always been straightforward. However, getting your API responses to follow a proper specification has usually been a different story. In most cases, that meant either pulling in a third-party package or writing a lot of boilerplate code manually. As a result, things could quickly become messy and inconsistent. Fortunately, Laravel 13 changes that. Now, it ships with first-party Laravel JSON:API support through a new JsonApiResource class. Because of this, you can generate spec-compliant responses without any extra setup. Not only that, but it also includes the correct Content-Type headers, relationships, sparse fieldsets, and links by default.
In other words, everything just works right out of the box—no hacks, no heavy packages, and no unnecessary complexity.
Let’s build one step by step.
What Is JSON:API?
The JSON:API specification is a widely adopted standard for structuring API responses. Essentially, it answers key questions like: Where should relationships go? How can clients request only specific fields? And what should the overall response envelope look like?
In the past, achieving JSON:API compliance in Laravel wasn’t always straightforward. Typically, developers had to rely on packages like Fractal or implement the specification manually. As a result, this often introduced extra complexity and inconsistency across projects.
However, with Laravel 13, things have changed significantly.
Now, JSON:API support is a native, first-party feature. Because of this, you no longer need external packages or custom implementations. Instead, you get a clean, standardized approach built directly into the framework—making your APIs more consistent, maintainable, and easier to scale.
Step 1 — Generate the JSON:API Resource
Run the make:resource command with the --json-api flag:
php artisan make:resource PostResource --json-api
This generates a new file at app/Http/Resources/PostResource.php that extends JsonApiResource:
<?php
namespace App\Http\Resources;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\JsonApi\JsonApiResource;
class PostResource extends JsonApiResource
{
/**
* The resource's attributes.
*/
public $attributes = [
// define your attributes here
];
/**
* The resource's relationships.
*/
public $relationships = [
// define your relationships here
];
}
That’s all the setup needed to start building Laravel JSON:API resources.
Step 2 — Define Attributes
Add your model attributes to the $attributes array. Laravel reads them directly from the model:
public $attributes = [
'title',
'body',
'created_at',
];
For more control, override the toAttributes() method instead. This is also where you can add computed values using closures — they are only evaluated when actually needed:
public function toAttributes(Request $request): array
{
return [
'title' => $this->title,
'body' => $this->body,
'is_published' => fn () => $this->published_at !== null,
'created_at' => $this->created_at,
];
}
Notice the closure on is_published. It is lazy, so it only runs if the client actually requests that field. This is one of the small but powerful optimizations built into JsonApiResource Laravel.
Step 3 — Define Relationships
Relationships in Laravel JSON:API resources are only serialized when the client explicitly requests them via the include query parameter. By default, this means relationships are not included in the response. As a result, your API avoids unnecessary data transfer right from the start.
In practice, this approach gives full control to the client. For example, if a client needs related data, it can simply request it using ?include=.... Otherwise, the response remains clean and minimal.
Because of this, you prevent over-fetching without adding extra logic on your end. At the same time, you still retain the flexibility to serve complex, relationship-rich responses when needed.
In other words, instead of always sending everything—or manually managing what to exclude—you let the client decide what it needs. Consequently, your API becomes more efficient, predictable, and scalable, especially as your data and relationships grow.
Add relationship names to the $relationships array:
use App\Http\Resources\UserResource;
public $relationships = [
'author' => UserResource::class,
'comments',
];
When a client sends ?include=author,comments, the response automatically includes the full related resources in a top-level included array — exactly as the JSON:API spec requires.
For conditional logic on relationships, use toRelationships() instead:
public function toRelationships(Request $request): array
{
return [
'author' => UserResource::class,
'comments' => fn () => CommentResource::collection(
$this->comments->where('is_public', true)
),
];
}
Step 4 — Return from a Route or Controller
Laravel JSON:API resources work exactly like standard API resources. Return them directly from a route or controller:
use App\Http\Resources\PostResource;
use App\Models\Post;
Route::get('/api/posts/{post}', function (Post $post) {
return new PostResource($post);
});
Or use the toResource() shorthand on the model:
Route::get('/api/posts/{post}', function (Post $post) {
return $post->toResource();
});
The Laravel JSON:API response automatically looks like this:
{
"data": {
"id": "1",
"type": "posts",
"attributes": {
"title": "Hello World",
"body": "This is my first post."
}
}
}
The Content-Type header is automatically set to application/vnd.api+json. No manual configuration needed.
Step 5 — Sparse Fieldsets
One of the best features of Laravel JSON:API is sparse fieldsets. Clients can request only the specific fields they need — reducing payload size significantly.
The client sends a request like this:
GET /api/posts?fields[posts]=title,created_at
And JsonApiResource in Laravel handles the filtering automatically. In other words, you don’t need to write any additional logic to control which fields are returned. By default, everything is streamlined for you.
For instance, when a client requests specific fields using sparse fieldsets, Laravel takes care of the filtering behind the scenes. As a result, only the requested attributes—like title and created_at—will be included in the response.
Because of this, you can avoid writing repetitive conditional code. At the same time, your API remains clean, predictable, and aligned with the JSON:API specification. Ultimately, this reduces boilerplate while still giving clients precise control over the data they receive.
Step 6 — Add Links and Meta
You can add links and meta to any Laravel JSON:API resource by overriding toLinks() and toMeta():
public function toLinks(Request $request): array
{
return [
'self' => route('api.posts.show', $this->resource),
];
}
public function toMeta(Request $request): array
{
return [
'readable_created_at' => $this->created_at->diffForHumans(),
];
}
This adds links and meta keys to the resource object in the response — fully compliant with the JSON:API spec.
Real-World Response Example
Here is what a full Laravel JSON:API response looks like when a client requests ?include=author:
{
"data": {
"id": "1",
"type": "posts",
"attributes": {
"title": "Hello World",
"body": "This is my first post."
},
"relationships": {
"author": {
"data": { "id": "1", "type": "users" }
}
},
"links": {
"self": "https://example.com/api/posts/1"
}
},
"included": [
{
"id": "1",
"type": "users",
"attributes": {
"name": "Umesh Rana"
}
}
]
}
This is a fully spec-compliant Laravel JSON:API response. And it took less than 30 lines of PHP to produce it.
Final Thoughts
Laravel JSON:API resources remove one of the biggest friction points in API development—inconsistent response formats. Traditionally, developers had to manually shape responses, which often led to differences across endpoints. As a result, maintaining consistency became a challenge over time. However, with JsonApiResource, that problem largely disappears. Now, your API speaks a standardized language that any client or frontend framework can understand right out of the box. Because of this, integration becomes smoother, development becomes faster, and your API stays predictable. In other words, instead of reinventing the response structure every time, you follow a clear and consistent standard from the start.
Moreover, features like sparse fieldsets, lazy attribute evaluation, and automatic relationship inclusion make JsonApiResource Laravel not just spec-compliant — but genuinely production-ready.
If you are building any kind of API in Laravel 13, this is the right way to do it.

Leave a Reply