Introduction
Laravel provides an expressive, minimal API around the Guzzle HTTP client, allowing you to quickly make outgoing HTTP requests to communicate with other web applications. Laravel's wrapper around Guzzle is focused on its most common use cases and a wonderful developer experience.
Before getting started, you should ensure that you have installed the Guzzle package as a dependency of your application. By default, Laravel automatically includes this dependency. However, if you have previously removed the package, you may install it again via Composer:
composer require guzzlehttp/guzzle
Making Requests
To make requests, you may use the head
,
get
, post
, put
,
patch
, and delete
methods
provided by the Http
facade. First, let's
examine how to make a basic GET
request to
another URL:
use Illuminate\Support\Facades\Http;
$response = Http::get('http://example.com');
The get
method returns an instance of
Illuminate\Http\Client\Response
, which
provides a variety of methods that may be used to
inspect the response:
$response->body() : string;
$response->json($key = null, $default = null) : array|mixed;
$response->object() : object;
$response->collect($key = null) : Illuminate\Support\Collection;
$response->status() : int;
$response->successful() : bool;
$response->redirect(): bool;
$response->failed() : bool;
$response->clientError() : bool;
$response->header($header) : string;
$response->headers() : array;
The Illuminate\Http\Client\Response
object
also implements the PHP ArrayAccess
interface, allowing you to access JSON response data
directly on the response:
return Http::get('http://example.com/users/1')['name'];
In addition to the response methods listed above, the following methods may be used to determine if the response has a given status code:
$response->ok() : bool; // 200 OK
$response->created() : bool; // 201 Created
$response->accepted() : bool; // 202 Accepted
$response->noContent() : bool; // 204 No Content
$response->movedPermanently() : bool; // 301 Moved Permanently
$response->found() : bool; // 302 Found
$response->badRequest() : bool; // 400 Bad Request
$response->unauthorized() : bool; // 401 Unauthorized
$response->paymentRequired() : bool; // 402 Payment Required
$response->forbidden() : bool; // 403 Forbidden
$response->notFound() : bool; // 404 Not Found
$response->requestTimeout() : bool; // 408 Request Timeout
$response->conflict() : bool; // 409 Conflict
$response->unprocessableEntity() : bool; // 422 Unprocessable Entity
$response->tooManyRequests() : bool; // 429 Too Many Requests
$response->serverError() : bool; // 500 Internal Server Error
URI Templates
The HTTP client also allows you to construct request URLs
using the URI
template specification. To define the URL
parameters that can be expanded by your URI template,
you may use the withUrlParameters
method:
Http::withUrlParameters([
'endpoint' => 'https://laravel.com',
'page' => 'docs',
'version' => '9.x',
'topic' => 'validation',
])->get('{ endpoint}/{page}/{version}/{topic}');
Dumping Requests
If you would like to dump the outgoing request instance
before it is sent and terminate the script's execution,
you may add the dd
method to the beginning
of your request definition:
return Http::dd()->get('http://example.com');
Request Data
Of course, it is common when making POST
,
PUT
, and PATCH
requests to
send additional data with your request, so these methods
accept an array of data as their second argument. By
default, data will be sent using the
application/json
content type:
use Illuminate\Support\Facades\Http;
$response = Http::post('http://example.com/users', [
'name' => 'Steve',
'role' => 'Network Administrator',
]);
GET Request Query Parameters
When making GET
requests, you may either
append a query string to the URL directly or pass an
array of key / value pairs as the second argument to the
get
method:
$response = Http::get('http://example.com/users', [
'name' => 'Taylor',
'page' => 1,
]);
Alternatively, the withQueryParameters
method may be used:
Http::retry(3, 100)->withQueryParameters([
'name' => 'Taylor',
'page' => 1,
])->get('http://example.com/users')
Sending Form URL Encoded Requests
If you would like to send data using the
application/x-www-form-urlencoded
content
type, you should call the asForm
method
before making your request:
$response = Http::asForm()->post('http://example.com/users', [
'name' => 'Sara',
'role' => 'Privacy Consultant',
]);
Sending a Raw Request Body
You may use the withBody
method if you would
like to provide a raw request body when making a
request. The content type may be provided via the
method's second argument:
$response = Http::withBody(
base64_encode($photo), 'image/jpeg'
)->post('http://example.com/photo');
Multi-Part Requests
If you would like to send files as multi-part requests,
you should call the attach
method before
making your request. This method accepts the name of the
file and its contents. If needed, you may provide a
third argument which will be considered the file's
filename, while a fourth argument may be used to provide
headers associated with the file:
$response = Http::attach(
'attachment', file_get_contents('photo.jpg'), 'photo.jpg', ['Content-Type' => 'image/jpeg']
)->post('http://example.com/attachments');
Instead of passing the raw contents of a file, you may pass a stream resource:
$photo = fopen('photo.jpg', 'r');
$response = Http::attach(
'attachment', $photo, 'photo.jpg'
)->post('http://example.com/attachments');
Headers
Headers may be added to requests using the
withHeaders
method. This
withHeaders
method accepts an array of key
/ value pairs:
$response = Http::withHeaders([
'X-First' => 'foo',
'X-Second' => 'bar'
])->post('http://example.com/users', [
'name' => 'Taylor',
]);
You may use the accept
method to specify the
content type that your application is expecting in
response to your request:
$response = Http::accept('application/json')->get('http://example.com/users');
For convenience, you may use the acceptJson
method to quickly specify that your application expects
the application/json
content type in
response to your request:
$response = Http::acceptJson()->get('http://example.com/users');
The withHeaders
method merges new headers
into the request's existing headers. If needed, you may
replace all of the headers entirely using the
replaceHeaders
method:
$response = Http::withHeaders([
'X-Original' => 'foo',
])->replaceHeaders([
'X-Replacement' => 'bar',
])->post('http://example.com/users', [
'name' => 'Taylor',
]);
Authentication
You may specify basic and digest authentication
credentials using the withBasicAuth
and
withDigestAuth
methods, respectively:
// Basic authentication...
$response = Http::withBasicAuth('taylor@laravel.com', 'secret')->post(/* ... */);
// Digest authentication...
$response = Http::withDigestAuth('taylor@laravel.com', 'secret')->post(/* ... */);
Bearer Tokens
If you would like to quickly add a bearer token to the
request's Authorization
header, you may use
the withToken
method:
$response = Http::withToken('token')->post(/* ... */);
Timeout
The timeout
method may be used to specify
the maximum number of seconds to wait for a response. By
default, the HTTP client will timeout after 30
seconds:
$response = Http::timeout(3)->get(/* ... */);
If the given timeout is exceeded, an instance of
Illuminate\Http\Client\ConnectionException
will be thrown.
You may specify the maximum number of seconds to wait
while trying to connect to a server using the
connectTimeout
method:
$response = Http::connectTimeout(3)->get(/* ... */);
Retries
If you would like the HTTP client to automatically retry
the request if a client or server error occurs, you may
use the retry
method. The
retry
method accepts the maximum number of
times the request should be attempted and the number of
milliseconds that Laravel should wait in between
attempts:
$response = Http::retry(3, 100)->post(/* ... */);
If you would like to manually calculate the number of
milliseconds to sleep between attempts, you may pass a
closure as the second argument to the retry
method:
use Exception;
$response = Http::retry(3, function (int $attempt, Exception $exception) {
return $attempt * 100;
})->post(/* ... */);
For convenience, you may also provide an array as the
first argument to the retry
method. This
array will be used to determine how many milliseconds to
sleep between subsequent attempts:
$response = Http::retry([100, 200])->post(/* ... */);
If needed, you may pass a third argument to the
retry
method. The third argument should be
a callable that determines if the retries should
actually be attempted. For example, you may wish to only
retry the request if the initial request encounters an
ConnectionException
:
use Exception;
use Illuminate\Http\Client\PendingRequest;
$response = Http::retry(3, 100, function (Exception $exception, PendingRequest $request) {
return $exception instanceof ConnectionException;
})->post(/* ... */);
If a request attempt fails, you may wish to make a change
to the request before a new attempt is made. You can
achieve this by modifying the request argument provided
to the callable you provided to the retry
method. For example, you might want to retry the request
with a new authorization token if the first attempt
returned an authentication error:
use Exception;
use Illuminate\Http\Client\PendingRequest;
use Illuminate\Http\Client\RequestException;
$response = Http::withToken($this->getToken())->retry(2, 0, function (Exception $exception, PendingRequest $request) {
if (! $exception instanceof RequestException || $exception->response->status() !== 401) {
return false;
}
$request->withToken($this->getNewToken());
return true;
})->post(/* ... */);
If all of the requests fail, an instance of
Illuminate\Http\Client\RequestException
will be thrown. If you would like to disable this
behavior, you may provide a throw
argument
with a value of false
. When disabled, the
last response received by the client will be returned
after all retries have been attempted:
$response = Http::retry(3, 100, throw: false)->post(/* ... */);
Warning!
If all of the requests fail because of a connection issue, aIlluminate\Http\Client\ConnectionException
will still be thrown even when thethrow
argument is set tofalse
.
Error Handling
Unlike Guzzle's default behavior, Laravel's HTTP client
wrapper does not throw exceptions on client or server
errors (400
and 500
level
responses from servers). You may determine if one of
these errors was returned using the
successful
, clientError
, or
serverError
methods:
// Determine if the status code is >= 200 and < 300...
$response->successful();
// Determine if the status code is >= 400...
$response->failed();
// Determine if the response has a 400 level status code...
$response->clientError();
// Determine if the response has a 500 level status code...
$response->serverError();
// Immediately execute the given callback if there was a client or server error...
$response->onError(callable $callback);
Throwing Exceptions
If you have a response instance and would like to throw
an instance of
Illuminate\Http\Client\RequestException
if
the response status code indicates a client or server
error, you may use the throw
or
throwIf
methods:
use Illuminate\Http\Client\Response;
$response = Http::post(/* ... */);
// Throw an exception if a client or server error occurred...
$response->throw();
// Throw an exception if an error occurred and the given condition is true...
$response->throwIf($condition);
// Throw an exception if an error occurred and the given closure resolves to true...
$response->throwIf(fn (Response $response) => true);
// Throw an exception if an error occurred and the given condition is false...
$response->throwUnless($condition);
// Throw an exception if an error occurred and the given closure resolves to false...
$response->throwUnless(fn (Response $response) => false);
// Throw an exception if the response has a specific status code...
$response->throwIfStatus(403);
// Throw an exception unless the response has a specific status code...
$response->throwUnlessStatus(200);
return $response['user']['id'];
The Illuminate\Http\Client\RequestException
instance has a public $response
property
which will allow you to inspect the returned
response.
The throw
method returns the response
instance if no error occurred, allowing you to chain
other operations onto the throw
method:
return Http::post(/* ... */)->throw()->json();
If you would like to perform some additional logic before
the exception is thrown, you may pass a closure to the
throw
method. The exception will be thrown
automatically after the closure is invoked, so you do
not need to re-throw the exception from within the
closure:
use Illuminate\Http\Client\Response;
use Illuminate\Http\Client\RequestException;
return Http::post(/* ... */)->throw(function (Response $response, RequestException $e) {
// ...
})->json();
Guzzle Middleware
Since Laravel's HTTP client is powered by Guzzle, you may
take advantage of Guzzle
Middleware to manipulate the outgoing request or
inspect the incoming response. To manipulate the
outgoing request, register a Guzzle middleware via the
withRequestMiddleware
method:
use Illuminate\Support\Facades\Http;
use Psr\Http\Message\RequestInterface;
$response = Http::withRequestMiddleware(
function (RequestInterface $request) {
return $request->withHeader('X-Example', 'Value');
}
)->get('http://example.com');
Likewise, you can inspect the incoming HTTP response by
registering a middleware via the
withResponseMiddleware
method:
use Illuminate\Support\Facades\Http;
use Psr\Http\Message\ResponseInterface;
$response = Http::withResponseMiddleware(
function (ResponseInterface $response) {
$header = $response->getHeader('X-Example');
// ...
return $response;
}
)->get('http://example.com');
Global Middleware
Sometimes, you may want to register a middleware that
applies to every outgoing request and incoming response.
To accomplish this, you may use the
globalRequestMiddleware
and
globalResponseMiddleware
methods.
Typically, these methods should be invoked in the
boot
method of your application's
AppServiceProvider
:
use Illuminate\Support\Facades\Http;
Http::globalRequestMiddleware(fn ($request) => $request->withHeader(
'User-Agent', 'Example Application/1.0'
));
Http::globalResponseMiddleware(fn ($response) => $response->withHeader(
'X-Finished-At', now()->toDateTimeString()
));
Guzzle Options
You may specify additional Guzzle
request options using the
withOptions
method. The
withOptions
method accepts an array of key
/ value pairs:
$response = Http::withOptions([
'debug' => true,
])->get('http://example.com/users');
Concurrent Requests
Sometimes, you may wish to make multiple HTTP requests concurrently. In other words, you want several requests to be dispatched at the same time instead of issuing the requests sequentially. This can lead to substantial performance improvements when interacting with slow HTTP APIs.
Thankfully, you may accomplish this using the
pool
method. The pool
method
accepts a closure which receives an
Illuminate\Http\Client\Pool
instance,
allowing you to easily add requests to the request pool
for dispatching:
use Illuminate\Http\Client\Pool;
use Illuminate\Support\Facades\Http;
$responses = Http::pool(fn (Pool $pool) => [
$pool->get('http://localhost/first'),
$pool->get('http://localhost/second'),
$pool->get('http://localhost/third'),
]);
return $responses[0]->ok() &&
$responses[1]->ok() &&
$responses[2]->ok();
As you can see, each response instance can be accessed
based on the order it was added to the pool. If you
wish, you can name the requests using the
as
method, which allows you to access the
corresponding responses by name:
use Illuminate\Http\Client\Pool;
use Illuminate\Support\Facades\Http;
$responses = Http::pool(fn (Pool $pool) => [
$pool->as('first')->get('http://localhost/first'),
$pool->as('second')->get('http://localhost/second'),
$pool->as('third')->get('http://localhost/third'),
]);
return $responses['first']->ok();
Customizing Concurrent Requests
The pool
method cannot be chained with other
HTTP client methods such as the withHeaders
or middleware
methods. If you want to apply
custom headers or middleware to pooled requests, you
should configure those options on each request in the
pool:
use Illuminate\Http\Client\Pool;
use Illuminate\Support\Facades\Http;
$headers = [
'X-Example' => 'example',
];
$responses = Http::pool(fn (Pool $pool) => [
$pool->withHeaders($headers)->get('http://laravel.test/test'),
$pool->withHeaders($headers)->get('http://laravel.test/test'),
$pool->withHeaders($headers)->get('http://laravel.test/test'),
]);
Macros
The Laravel HTTP client allows you to define
"macros", which can serve as a fluent,
expressive mechanism to configure common request paths
and headers when interacting with services throughout
your application. To get started, you may define the
macro within the boot
method of your
application's
App\Providers\AppServiceProvider
class:
use Illuminate\Support\Facades\Http;
/**
* Bootstrap any application services.
*/
public function boot(): void
{
Http::macro('github', function () {
return Http::withHeaders([
'X-Example' => 'example',
])->baseUrl('https://github.com');
});
}
Once your macro has been configured, you may invoke it from anywhere in your application to create a pending request with the specified configuration:
$response = Http::github()->get('/');
Testing
Many Laravel services provide functionality to help you
easily and expressively write tests, and Laravel's HTTP
client is no exception. The Http
facade's
fake
method allows you to instruct the HTTP
client to return stubbed / dummy responses when requests
are made.
Faking Responses
For example, to instruct the HTTP client to return empty,
200
status code responses for every
request, you may call the fake
method with
no arguments:
use Illuminate\Support\Facades\Http;
Http::fake();
$response = Http::post(/* ... */);
Faking Specific URLs
Alternatively, you may pass an array to the
fake
method. The array's keys should
represent URL patterns that you wish to fake and their
associated responses. The *
character may
be used as a wildcard character. Any requests made to
URLs that have not been faked will actually be executed.
You may use the Http
facade's
response
method to construct stub / fake
responses for these endpoints:
Http::fake([
// Stub a JSON response for GitHub endpoints...
'github.com/*' => Http::response(['foo' => 'bar'], 200, $headers),
// Stub a string response for Google endpoints...
'google.com/*' => Http::response('Hello World', 200, $headers),
]);
If you would like to specify a fallback URL pattern that
will stub all unmatched URLs, you may use a single
*
character:
Http::fake([
// Stub a JSON response for GitHub endpoints...
'github.com/*' => Http::response(['foo' => 'bar'], 200, ['Headers']),
// Stub a string response for all other endpoints...
'*' => Http::response('Hello World', 200, ['Headers']),
]);
Faking Response Sequences
Sometimes you may need to specify that a single URL
should return a series of fake responses in a specific
order. You may accomplish this using the
Http::sequence
method to build the
responses:
Http::fake([
// Stub a series of responses for GitHub endpoints...
'github.com/*' => Http::sequence()
->push('Hello World', 200)
->push(['foo' => 'bar'], 200)
->pushStatus(404),
]);
When all the responses in a response sequence have been
consumed, any further requests will cause the response
sequence to throw an exception. If you would like to
specify a default response that should be returned when
a sequence is empty, you may use the
whenEmpty
method:
Http::fake([
// Stub a series of responses for GitHub endpoints...
'github.com/*' => Http::sequence()
->push('Hello World', 200)
->push(['foo' => 'bar'], 200)
->whenEmpty(Http::response()),
]);
If you would like to fake a sequence of responses but do
not need to specify a specific URL pattern that should
be faked, you may use the
Http::fakeSequence
method:
Http::fakeSequence()
->push('Hello World', 200)
->whenEmpty(Http::response());
Fake Callback
If you require more complicated logic to determine what
responses to return for certain endpoints, you may pass
a closure to the fake
method. This closure
will receive an instance of
Illuminate\Http\Client\Request
and should
return a response instance. Within your closure, you may
perform whatever logic is necessary to determine what
type of response to return:
use Illuminate\Http\Client\Request;
Http::fake(function (Request $request) {
return Http::response('Hello World', 200);
});
Preventing Stray Requests
If you would like to ensure that all requests sent via
the HTTP client have been faked throughout your
individual test or complete test suite, you can call the
preventStrayRequests
method. After calling
this method, any requests that do not have a
corresponding fake response will throw an exception
rather than making the actual HTTP request:
use Illuminate\Support\Facades\Http;
Http::preventStrayRequests();
Http::fake([
'github.com/*' => Http::response('ok'),
]);
// An "ok" response is returned...
Http::get('https://github.com/laravel/framework');
// An exception is thrown...
Http::get('https://laravel.com');
Inspecting Requests
When faking responses, you may occasionally wish to
inspect the requests the client receives in order to
make sure your application is sending the correct data
or headers. You may accomplish this by calling the
Http::assertSent
method after calling
Http::fake
.
The assertSent
method accepts a closure
which will receive an
Illuminate\Http\Client\Request
instance and
should return a boolean value indicating if the request
matches your expectations. In order for the test to
pass, at least one request must have been issued
matching the given expectations:
use Illuminate\Http\Client\Request;
use Illuminate\Support\Facades\Http;
Http::fake();
Http::withHeaders([
'X-First' => 'foo',
])->post('http://example.com/users', [
'name' => 'Taylor',
'role' => 'Developer',
]);
Http::assertSent(function (Request $request) {
return $request->hasHeader('X-First', 'foo') &&
$request->url() == 'http://example.com/users' &&
$request['name'] == 'Taylor' &&
$request['role'] == 'Developer';
});
If needed, you may assert that a specific request was not
sent using the assertNotSent
method:
use Illuminate\Http\Client\Request;
use Illuminate\Support\Facades\Http;
Http::fake();
Http::post('http://example.com/users', [
'name' => 'Taylor',
'role' => 'Developer',
]);
Http::assertNotSent(function (Request $request) {
return $request->url() === 'http://example.com/posts';
});
You may use the assertSentCount
method to
assert how many requests were "sent" during
the test:
Http::fake();
Http::assertSentCount(5);
Or, you may use the assertNothingSent
method
to assert that no requests were sent during the
test:
Http::fake();
Http::assertNothingSent();
Recording Requests / Responses
You may use the recorded
method to gather
all requests and their corresponding responses. The
recorded
method returns a collection of
arrays that contains instances of
Illuminate\Http\Client\Request
and
Illuminate\Http\Client\Response
:
Http::fake([
'https://laravel.com' => Http::response(status: 500),
'https://nova.laravel.com/' => Http::response(),
]);
Http::get('https://laravel.com');
Http::get('https://nova.laravel.com/');
$recorded = Http::recorded();
[$request, $response] = $recorded[0];
Additionally, the recorded
method accepts a
closure which will receive an instance of
Illuminate\Http\Client\Request
and
Illuminate\Http\Client\Response
and may be
used to filter request / response pairs based on your
expectations:
use Illuminate\Http\Client\Request;
use Illuminate\Http\Client\Response;
Http::fake([
'https://laravel.com' => Http::response(status: 500),
'https://nova.laravel.com/' => Http::response(),
]);
Http::get('https://laravel.com');
Http::get('https://nova.laravel.com/');
$recorded = Http::recorded(function (Request $request, Response $response) {
return $request->url() !== 'https://laravel.com' &&
$response->successful();
});
Events
Laravel fires three events during the process of sending
HTTP requests. The RequestSending
event is
fired prior to a request being sent, while the
ResponseReceived
event is fired after a
response is received for a given request. The
ConnectionFailed
event is fired if no
response is received for a given request.
The RequestSending
and
ConnectionFailed
events both contain a
public $request
property that you may use
to inspect the
Illuminate\Http\Client\Request
instance.
Likewise, the ResponseReceived
event
contains a $request
property as well as a
$response
property which may be used to
inspect the Illuminate\Http\Client\Response
instance. You may register event listeners for this
event in your
App\Providers\EventServiceProvider
service
provider:
/**
* The event listener mappings for the application.
*
* @var array
*/
protected $listen = [
'Illuminate\Http\Client\Events\RequestSending' => [
'App\Listeners\LogRequestSending',
],
'Illuminate\Http\Client\Events\ResponseReceived' => [
'App\Listeners\LogResponseReceived',
],
'Illuminate\Http\Client\Events\ConnectionFailed' => [
'App\Listeners\LogConnectionFailed',
],
];