Encodera

Thanks for reading!

Donate to the site — we work hard for you.

Contents

Calling other services

Exchange rates, payments, delivery, social login — a site talks to other people's APIs all the time. Symfony has the HttpClient component for that: it sends requests and parses responses.

A GET request

use Symfony\Contracts\HttpClient\HttpClientInterface;
 
#[Route('/rates', name: 'rates')]
public function rates(HttpClientInterface $client): Response
{
    $response = $client->request('GET', 'https://api.example.com/rates');
 
    $data = $response->toArray();
 
    return $this->render('rates.html.twig', ['rates' => $data['rates']]);
}

toArray() turns a JSON response into an array.

What a response tells you

MethodWhat it returns
getStatusCode()The status code
getHeaders()The headers
getContent()The body as a string
toArray()The parsed JSON

Address parameters

$response = $client->request('GET', 'https://api.example.com/users', [
    'query' => ['page' => 2, 'role' => 'admin'],
]);

The address becomes /users?page=2&role=admin, with the values encoded.

A POST request

$response = $client->request('POST', 'https://api.example.com/orders', [
    'json' => ['product_id' => 42, 'quantity' => 2],
]);
 
$order = $response->toArray();

The json key encodes the data and adds the Content-Type header. A regular form uses the body key.

Headers and authorization

$response = $client->request('GET', 'https://api.example.com/me', [
    'auth_bearer' => $token,
    'headers' => ['Accept' => 'application/json'],
    'timeout' => 5,
]);

timeout limits the wait: without it a slow service holds up your page too.

Errors

If the service answers with a 4xx or 5xx code, toArray() and getContent() throw an exception. A network failure is an exception as well:

use Symfony\Contracts\HttpClient\Exception\HttpExceptionInterface;
use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface;
 
try {
    $data = $client->request('GET', $url)->toArray();
} catch (HttpExceptionInterface $e) {
    $code = $e->getResponse()->getStatusCode();
    // the service answered with an error
} catch (TransportExceptionInterface $e) {
    // the service is unavailable
}

A scoped client

The common parameters of one API are set in the configuration:

# config/packages/framework.yaml
framework:
    http_client:
        scoped_clients:
            github.client:
                base_uri: 'https://api.github.com'
                headers:
                    Accept: 'application/vnd.github+json'
                    Authorization: 'Bearer %env(GITHUB_TOKEN)%'

The argument name selects the client:

public function __construct(private HttpClientInterface $githubClient) {}
 
public function repos(string $user): array
{
    return $this->githubClient->request('GET', "/users/$user/repos")->toArray();
}

Common mistake

A request is sent not when request() is called but when you access the response. If you never call getStatusCode(), getContent() or toArray(), a service error goes unnoticed until the object is destroyed. Always read the response and wrap a call to someone else's service in try: sooner or later it will be unavailable.

Thanks for reading!

Donate to the site — we work hard for you.