bigz/halapi
HAL JSON representation helper for HATEOAS APIs. Uses annotations and conventions to expose entities with links/relations, with pluggable URL generator, annotation reader, object manager, and pagination. Symfony-friendly today; aiming for framework-agnostic/PSR.
Installation
composer require bigz/halapi
Add to config/app.php under providers:
Bigz\Halapi\HalapiServiceProvider::class,
Basic Usage Define a HAL resource in a controller or service:
use Bigz\Halapi\Resource;
class UserController extends Controller
{
public function show($id)
{
$user = User::findOrFail($id);
return Resource::make('users', $user)
->add('self', route('users.show', $user))
->add('posts', route('users.posts', $user));
}
}
First Use Case Return a HAL-formatted API response with embedded links and relationships:
return Resource::collection('users', User::all())
->add('self', route('users.index'))
->add('create', route('users.store'));
Single Resource
Resource::make('resource-name', $model)
->add('link-name', '/url')
->embed('related', $relatedResource);
Collections
Resource::collection('resource-name', $collection)
->add('self', route('resource.index'))
->paginate(10); // Optional pagination
$user = User::with('posts')->find(1);
return Resource::make('users', $user)
->embed('posts', $user->posts);
Override Defaults
Resource::make('users', $user)
->setEmbeddedKey('_embedded'); // Default is '_embedded'
->setLinksKey('_links'); // Default is '_links'
Dynamic Link Generation
$resource->add('self', function ($user) {
return route('users.show', $user);
});
// In routes/api.php
Route::prefix('v1')->group(function () {
Route::get('/users/{id}', [UserController::class, 'show']);
});
API Resources
Combine with Laravel's JsonResource for hybrid responses:
return Resource::make('users', UserResource::make($user));
Form Requests
Validate HAL-specific fields (e.g., _links):
public function rules()
{
return [
'_links.self' => 'required|url',
];
}
Circular References Avoid embedding deeply nested resources to prevent infinite loops:
// Bad: $user->posts->comments->user...
// Good: Limit depth or use `->without('comments')`.
Link Validation
Ensure all _links are absolute URLs or valid relative paths. HALAPI does not validate them by default.
Pagination Quirks
Paginated collections require explicit ->paginate():
// Missing pagination will return all items
Resource::collection('users', User::paginate(10));
Inspect Raw Output
Use dd($resource->toArray()) to debug structure before returning.
Check for Missing Keys
If _links or _embedded are missing, verify:
$resource->hasLinks(); // Returns bool
$resource->hasEmbedded(); // Returns bool
Custom Embedders
Override Bigz\Halapi\Embedder to modify embedding logic:
class CustomEmbedder extends Embedder
{
public function embed($resource, $key, $value)
{
// Custom logic
return parent::embed($resource, $key, $value);
}
}
Middleware for HAL Responses Add middleware to enforce HAL format:
namespace App\Http\Middleware;
class EnforceHalFormat
{
public function handle($request, Closure $next)
{
$response = $next($request);
if ($response->isJson() && !Halapi::isHal($response->getData())) {
abort(406, 'HAL format required');
}
return $response;
}
}
No Built-in Caching Manually cache responses if needed:
return Cache::remember("hal_{$id}", now()->addHours(1), function () use ($user) {
return Resource::make('users', $user);
});
Deprecated Methods
Avoid Resource::item() (use Resource::make() instead) and Resource::items() (use Resource::collection()).
Lazy Embedding
Use ->embedWhen() to conditionally embed:
$resource->embedWhen($user->hasPosts(), 'posts', $user->posts);
Minimize Embedded Data Select only necessary fields:
$resource->embed('posts', $user->posts->load('title'));
How can I help you explore Laravel packages today?