Responses
The toolkit provides two response types: SuccessResponse for data and ErrorResponse for errors. Both implement Laravel's Responsable interface, so you can return them directly from controllers.
Success Responses
Single Resource
use App\Http\Resources\ProductResource;
use App\Models\Product;
use BlueBeetle\ApiToolkit\Http\Response;
class ProductController
{
public function show(Product $product, Response $response)
{
return $response->success($product, ProductResource::class);
}
}
Returns:
{
"data": {
"type": "products",
"id": "1",
"attributes": {
"name": "Widget",
"price": 29.99
}
}
}
Collections
use App\Http\Resources\ProductResource;
use App\Models\Product;
use BlueBeetle\ApiToolkit\Http\Response;
class ProductController
{
public function index(Response $response)
{
$products = Product::all();
return $response->success($products, ProductResource::class);
}
}
Adding Meta and Links
use App\Http\Resources\ProductResource;
use App\Models\Product;
use BlueBeetle\ApiToolkit\Http\Response;
class ProductController
{
public function show(Product $product, Response $response)
{
return $response->success($product, ProductResource::class)
->meta(['request_id' => $requestId])
->links(['self' => route('api.products.show', $product)]);
}
}
Merging Meta and Links
Calling meta() or links() multiple times merges the values rather than replacing them. This is useful when different parts of your code need to add their own metadata:
use App\Http\Resources\ProductResource;
use App\Models\Product;
use BlueBeetle\ApiToolkit\Http\Response;
class ProductController
{
public function show(Product $product, Response $response)
{
return $response->success($product, ProductResource::class)
->meta(['request_id' => 'req_abc123'])
->meta(['cache_hit' => false]) // merged with previous meta
->links(['self' => route('api.products.show', $product)])
->links(['related' => route('api.categories.show', $product->category_id)]); // merged
}
}
Custom Status Code and Headers
use App\Http\Resources\ProductResource;
use App\Models\Product;
use BlueBeetle\ApiToolkit\Http\Response;
use Illuminate\Http\Request;
class ProductController
{
public function store(Request $request, Response $response)
{
$product = Product::create($request->validated());
return $response->success($product, ProductResource::class)
->respond(201, ['X-Custom-Header' => 'value']);
}
}
Raw Array Data
For responses that don't map to a resource, pass the data directly:
use BlueBeetle\ApiToolkit\Http\Response;
class HealthController
{
public function __invoke(Response $response)
{
return $response->success([
'message' => 'Import complete',
'rows_processed' => 42,
]);
}
}
No Content (204)
For delete operations or actions that return no body:
use App\Models\Product;
use BlueBeetle\ApiToolkit\Http\Response;
class ProductController
{
public function destroy(Product $product, Response $response)
{
$product->delete();
return $response->noContent();
}
}
Returns a 204 No Content response with an empty body and the application/vnd.api+json content type.
Error Responses
Basic Error
use App\Http\Resources\ProductResource;
use App\Models\Product;
use BlueBeetle\ApiToolkit\Http\Response;
class ProductController
{
public function show(string $id, Response $response)
{
$product = Product::find($id);
if ($product === null) {
return $response->error('Not Found', 'The requested product does not exist.', 404);
}
return $response->success($product, ProductResource::class);
}
}
Returns:
{
"errors": [
{
"status": "404",
"title": "Not Found",
"detail": "The requested product does not exist."
}
]
}
With Code and Source
use BlueBeetle\ApiToolkit\Http\Response;
class ProductController
{
public function update(string $id, Response $response)
{
return $response->error('Validation Failed', 'The name field is required.', 422)
->code('VALIDATION_ERROR')
->source(['pointer' => '/data/attributes/name']);
}
}
With Meta
use BlueBeetle\ApiToolkit\Http\Response;
class RateLimitedController
{
public function __invoke(Response $response)
{
return $response->error('Rate Limited', 'Too many requests.', 429)
->meta(['retry_after' => 60]);
}
}
Chaining Code, Source, and Meta
All ErrorResponse methods are chainable, so you can combine them in a single expression:
use BlueBeetle\ApiToolkit\Http\Response;
class PaymentController
{
public function charge(Response $response)
{
return $response->error('Payment Failed', 'Card was declined.', 402)
->code('CARD_DECLINED')
->source(['pointer' => '/data/attributes/card_number'])
->meta(['decline_code' => 'insufficient_funds', 'retry_allowed' => true]);
}
}
Using with QueryBuilder
The QueryBuilder returns SuccessResponse instances directly:
use App\Http\Resources\ProductResource;
use App\Models\Product;
use BlueBeetle\ApiToolkit\QueryBuilder;
use Illuminate\Http\Request;
class ProductController
{
public function index(Request $request)
{
return QueryBuilder::for(Product::class, $request)
->fromResource(ProductResource::class)
->paginate(); // offset pagination
}
public function all(Request $request)
{
return QueryBuilder::for(Product::class, $request)
->fromResource(ProductResource::class)
->get(); // all results, no pagination
}
}