paknahad/jsonapi-bundle
Laravel package for building JSON:API-compliant APIs. Provides controllers, resources and request handling to standardize responses, filtering, sorting, pagination, includes and errors, helping you ship consistent endpoints faster.
Installation
composer require paknahad/jsonapi-bundle
Add to config/app.php under providers:
Paknahad\JsonApiBundle\Providers\JsonApiServiceProvider::class,
Publish the config (optional):
php artisan vendor:publish --provider="Paknahad\JsonApiBundle\Providers\JsonApiServiceProvider" --tag=config
Basic API Endpoint
Define a resource in routes/api.php:
Route::get('/posts', [JsonApiController::class, 'index'])->name('posts.index');
Define a Resource
Create a resource class (e.g., app/Http/Resources/PostResource.php):
namespace App\Http\Resources;
use Paknahad\JsonApiBundle\Resources\Resource;
class PostResource extends Resource
{
public function toArray($request)
{
return [
'id' => $this->id,
'title' => $this->title,
'body' => $this->body,
];
}
}
Controller Integration
Use the JsonApiController trait or extend JsonApiController:
use Paknahad\JsonApiBundle\Http\Controllers\JsonApiController;
class PostController extends JsonApiController
{
public function index()
{
$posts = Post::all();
return $this->respondWithResourceCollection(PostResource::class, $posts);
}
}
return $this->respondWithResource(PostResource::class, $post);
return $this->respondWithResourceCollection(PostResource::class, $posts);
class PostResource extends Resource
{
public function toArray($request)
{
return [
'id' => $this->id,
'title' => $this->title,
'comments' => CommentResource::collection($this->comments),
];
}
}
return $this->respondWithRelationships(PostResource::class, $post, ['comments']);
class PostResource extends Resource
{
public function relationships()
{
return [
'comments' => function () {
return $this->comments()->with('author');
},
];
}
}
$query = Post::query();
if ($request->has('filter[title]')) {
$query->where('title', 'like', '%' . $request->input('filter[title]') . '%');
}
return $this->respondWithResourceCollection(PostResource::class, $query->paginate());
return $this->respondWithResourceCollection(
PostResource::class,
$posts,
['meta' => ['total' => $posts->total()]]
);
return $this->respondWithResourceCollection(
PostResource::class,
$posts,
null,
['include' => 'comments.author']
);
Route::prefix('v1')->group(function () {
Route::get('/posts', [PostController::class, 'index']);
});
class PostResource extends Resource
{
public function version()
{
return 'v1';
}
}
id Field
JSON:API requires id in every resource. Ensure your toArray() includes it:
return ['id' => $this->id, ...];
relationships() or toArray() includes proper links:
public function links()
{
return [
'self' => route('posts.show', $this->id),
'related' => route('comments.index', $this->id),
];
}
JsonApiPaginator for consistent pagination:
use Paknahad\JsonApiBundle\Pagination\JsonApiPaginator;
return JsonApiPaginator::make($posts, $posts->perPage());
JSONAPI_DEBUG in .env:
JSONAPI_DEBUG=true
This logs validation errors and schema mismatches.CHANGELOG.md for breaking changes (e.g., respond() → respondWithResource()).$posts = Post::with('comments.author')->get();
php artisan jsonapi:compile
Paknahad\JsonApiBundle\Serializers\Serializer for non-standard data types (e.g., dates):
class CustomDateSerializer extends Serializer
{
public function serialize($data)
{
return Carbon::parse($data)->toIso8601String();
}
}
Route::middleware(['jsonapi.validate'])->group(function () {
// Routes here
});
config/jsonapi.php:
'meta' => [
'default' => ['version' => '1.0'],
],
'strict' => env('JSONAPI_STRICT', false),
How can I help you explore Laravel packages today?