Creating an API integration using the Laravel Saloon package

Published: Posted by Patrick Henninger

The Laravel Saloon package is a powerful tool for creating API integrations for Laravel or any other PHP application. In this article, I will show you how to use this package to create a integration consisting of a connector, a request and a custom response type that can handle paginated results from an API using PHP's Generator feature.

A Laravel Saloon integration typically consists of the following parts:

  • The Connector: This is the base definition of the API and contains the base URL
  • A Request: This defines a single endpoint of the API as well as the data it does consume
  • A Response: This is generally a helper class that allows for simple interaction with response data. In this post I will guide you throught the creation of each part by implementing an Laravel Saloon Integration for the Star Wars API.

First, you will need to install the Laravel Saloon package using Composer. If you don't already have composer installed on your system, you can follow the instructions on the Composer website to get it set up. Once Composer is installed, you can run the following command to install the Laravel Saloon package in your project:

composer require sammyjo20/saloon

With the Laravel Saloon package installed, you are now ready to create your API integrations! The first step is to create a connector class. With Saloo, connectors are used to define the basic information about an API such as the endpoint or default headers. As previously mentioned, for this example we are going to implement the Star Wars API. To create a new connector you can use the following command:

php artisan saloon:connector StarWarsAPI StarWarsAPIConnector 

This will generate a StarWarsAPIConnector connector in the app/Http/Integrations directory that will have the following contents:

<?php

use Sammyjo20\Saloon\Http\SaloonConnector;

class StarWarsAPIConnector extends SaloonConnector
{
    public function defineBaseUrl(): string
    {
        return 'https://www.swapi.tech/api';
    }
}

Simply override the default value returned by the defineBaseUrl method with the base endpoint of the API. For this example we are going to implement the Star Wars API using the endpoint https://www.swapi.tech/api/.

To get any data from the API you will have to create a request. With Laravel Saloon following an object oriented and typed approach every endpoint of your API is defined as a dedicated request class. To create a Laravel Saloon request class, you can use the following artisan command - this command will generate a new request class in the app/Http/Integrations/<Integration>/Requests directory:

php artisan saloon:request StarWarsAPI GetPeople

This will generate a class that defined it's HTTP verb, connector and endpoint for your request. Note that the following code has already been adjusted to fit the /people endpoint of the Star Wars API with the page parameter.

<?php

namespace App\Http\Integrations\StarWarsAPI;

use App\Http\Integrations\StarWarsAPI\StarWarsAPIConnector;
use Sammyjo20\Saloon\Http\SaloonRequest;

class GetPeopleRequest extends SaloonRequest
{
    protected ?string $connector = StarWarsAPIConnector::class;

    protected ?string $method = 'GET';

    public function defineEndpoint(): string
    {
        return '/people';
    }

    public function defaultQuery(): array
    {
        return [
            'page' => 1, // &page=1
        ];
    }
}

We can send our request and get the response using the following code:


use App\Integrations\StarWarsAPI\Requests\GetPeopleRequest;

...

$response = (new GetPeopleRequest())->send();
// handle the response...

As you might have noted, the response of the /people endpoint does return not a full list of all people but a paginated result. As we could now manually check the response for the availability of a next URL property it would be smarter to implement this functionality in a dedicated PaginatedResponse custom response type. To create a custom response type you can use the following command:

php artisan saloon:response StarWarsAPI PaginatedResponse

Next we register the new response type with our request:

<?php

use Sammyjo20\Saloon\Http\SaloonRequest;

class GetPeopleRequest extends SaloonRequest
{
    // ...

    protected ?string $response = PaginatedResponse::class;
    
    // ...
}

In the custom response class we are going to implement a new all method that does retrieve all people and return the data using PHP's Generator feature:

<?php

namespace App\Http\Integrations\StarWarsAPI\Responses;

use Sammyjo20\Saloon\Http\SaloonResponse;

class GetPeopleRequest extends SaloonResponse
{
    // ...

    protected ?string $response = PaginatedResponse::class;

    public function page(): int
    {
        return $this->originalRequest->getQuery('page') ?? 1;
    }

    public function prev(): ?string
    {
        return Arr::get($this, 'previous');
    }

    public function next(): ?string
    {
        return Arr::get($this, 'next');
    }

    /**
     * Helper to get iterate all pages and return a generator.
     * This can produce MANY API requests, make sure you do
     * handle rate limits.
     */
    public function all(): \Generator
    {
        /* Return current results first */
        yield Arr::get($this->json(), 'results', [});
        
        /* Iterate all following pages and yield the results */
        while ($nextRequest = $this->nextRequest()) {
            yield Arr::get($nextRequest->send()->json(), 'results', []);
        }
    }

    private function nextRequest(): SaloonRequest
    {
        if ($this->next()) {
            $request = $this->originalRequest::make();
            $request->addQuery('page', $this->page() + 1);
            return $request;
        }

        return null;
    }
    
    // ...
}

Note the use of PHP's Generator feature: This allows us the combat the memory consuming pattern of returning the result of an API request as a sometimes huge array - in some cases this can consume hundreds of megabytes of RAM! Using Generators we are now able to return the data as we receive it in chunks effectively eliminating any memory issues. To learn more about Generators you should look up the documentation in the PHP manual. Now we can easily retrieve all persons from the Star Wars API using just one line of code:


use App\Integrations\StarWarsAPI\Requests\GetPeopleRequest;

...

$people = (new GetPeopleRequest())->all();
foreach ($people as $person) {
    // do something ...
}

Make sure you do familiarize yourself with all the functionality offered by the Laravel Saloon package by reading the documentation.