Skip to content

Responses

The Response class wraps HTTP responses with typed accessors and convenience methods for parsing, validation, and transformation.

$response = $connector->send(new GetUserRequest(1));
$status = $response->status(); // 200, 404, 500, etc.
$response->ok(); // true if 2xx
$response->successful(); // alias for ok()
$response->redirect(); // true if 3xx
$response->clientError(); // true if 4xx
$response->serverError(); // true if 5xx
$response->failed(); // true if 4xx or 5xx
$data = $response->json();
$name = $response->json('user.name');
$email = $response->json('data.0.email');
$role = $response->json('user.role') ?? 'guest';
$object = $response->object();
echo $object->name;
$collection = $response->collect();
$users = $response->collect('data.users');
$activeUsers = $response->collect('users')
->where('active', true)
->pluck('email');
$body = $response->body();
class User
{
public function __construct(
public readonly int $id,
public readonly string $name,
public readonly string $email,
) {}
}
$user = $response->dto(User::class);
$users = $response->dtoCollection(User::class);
$users = $response->dtoCollection(User::class, 'data.users');
$contentType = $response->header('Content-Type');
$rateLimit = $response->header('X-RateLimit-Remaining');
$headers = $response->headers();
$response->throw(); // Throws if 4xx or 5xx
$data = $response->throw()->json();
if ($response->failed()) {
$error = $response->json('error.message');
throw new ApiException($error, $response->status());
}
$etag = $response->etag();
$lastModified = $response->lastModified();
if ($response->wasNotModified()) {
// Use cached version
}
if ($response->fromCache()) {
// Response was served from cache
}
$durationMs = $response->duration();
$rateLimit = $response->rateLimit();
if ($rateLimit) {
echo $rateLimit->limit;
echo $rateLimit->remaining;
echo $rateLimit->reset;
}
$traceId = $response->traceId();
$spanId = $response->spanId();
$key = $response->idempotencyKey();
if ($response->wasIdempotentReplay()) {
// This response was a replay
}
$response->saveTo('/path/to/file.pdf');
$response->streamTo('/path/to/large-file.zip', function ($downloaded, $total) {
$percent = $total > 0 ? ($downloaded / $total) * 100 : 0;
echo "Downloaded: {$percent}%\n";
});
$filename = $response->filename();
if ($response->isDownload()) {
$response->saveTo('/downloads/' . $response->filename());
}
$base64 = $response->base64();
foreach ($response->chunks(8192) as $chunk) {
fwrite($handle, $chunk);
}
$stream = $response->stream();
$newResponse = $response->withJson(['modified' => true]);
$newResponse = $response->withJsonKey('processed_at', now()->toIso8601String());
$newResponse = $response->withBody('new raw content');
$newResponse = $response->withHeaders(['X-Custom' => 'value']);
$newResponse = $response->withHeader('X-Custom', 'value');
$newResponse = $response->withStatus(201);
$request = $response->request();
echo $request->endpoint();
echo $request->method();
$psrResponse = $response->toPsrResponse();
use Cline\Relay\Core\Response;
$response = Response::make(
data: ['id' => 1, 'name' => 'John'],
status: 200,
headers: ['X-Custom' => 'value'],
);
$response->json('name'); // 'John'
$response->status(); // 200
$response->dump(); // Dump and continue
$response->dd(); // Dump and die
use Cline\Relay\Core\Response;
Response::macro('isRateLimited', function () {
return $this->status() === 429;
});
if ($response->isRateLimited()) {
$retryAfter = $response->header('Retry-After');
sleep((int) $retryAfter);
}
$response = $connector->send(new GetOrderRequest($orderId));
if ($response->failed()) {
if ($response->status() === 404) {
throw new OrderNotFoundException($orderId);
}
throw new ApiException(
$response->json('error.message'),
$response->status()
);
}
logger()->info('API call completed', [
'duration' => $response->duration(),
'cached' => $response->fromCache(),
'trace_id' => $response->traceId(),
]);
$rateLimit = $response->rateLimit();
if ($rateLimit && $rateLimit->remaining < 10) {
logger()->warning('Rate limit running low', [
'remaining' => $rateLimit->remaining,
'reset' => $rateLimit->reset,
]);
}
$order = $response->dto(Order::class);
$items = $response->dtoCollection(OrderItem::class, 'items');
if ($response->json('has_attachment')) {
$attachmentResponse = $connector->send(new GetOrderAttachmentRequest($orderId));
if ($attachmentResponse->isDownload()) {
$attachmentResponse->saveTo(
storage_path('orders/' . $attachmentResponse->filename())
);
}
}