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) : array|mixed;
$response->object() : object;
$response->collect($key = null) : Illuminate\Support\Collection;
$response->status() : int;
$response->ok() : bool;
$response->successful() : bool;
$response->redirect(): bool;
$response->failed() : bool;
$response->serverError() : 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'];
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,
]);
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:
$response = Http::attach(
'attachment', file_get_contents('photo.jpg'), 'photo.jpg'
)->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');
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:
$response = Http::timeout(3)->get(...);
If the given timeout is exceeded, an instance of
Illuminate\Http\Client\ConnectionException
will be
thrown.
Retries
If you would like 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 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
:
$response = Http::retry(3, 100, function ($exception) {
return $exception instanceof ConnectionException;
})->post(...);
If all of the requests fail, an instance of
Illuminate\Http\Client\RequestException
will be thrown.
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:
$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);
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:
return Http::post(...)->throw(function ($response, $e) {
//
})->json();
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();
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.
*
* @return void
*/
public function boot()
{
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 wrapper 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(...);
Note: When faking requests, HTTP client middleware are not executed. You should define expectations for faked responses as if these middleware have run correctly.
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 of 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:
Http::fake(function ($request) {
return Http::response('Hello World', 200);
});
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();
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',
],
];