Firing off an HTTP request

Now that you have your Guardian instance, we can wrap our HTTP request to let Guardian handle any rate-limiting or error-handling rules we may have. We'll look at Cache Drivers and Rules in more detail in later sections.

This package doesn't limit how you fire your HTTP requests. You can write any code you'd like to run to get your data in a closure in the send method. Let's assume for now the request is always successful.

use Midnite81\Guardian\Factories\GuardianFactory;
use Midnite81\Guardian\Store\RedisStore;
use Midnite81\Guardian\Rules\RateLimitRule;
use GuzzleHttp\Client;

$guardian = GuardianFactory::create(
    'blog-post-1',
    new RedisStore($options),
    [RateLimitRule::allow(6)->perMinute()],
);

$result = $guardian->send(function () {
    $client = new Client();
    $response = $client->get('https://jsonplaceholder.typicode.com/posts/1');
    return json_decode($response->getBody()->getContents(), true);
});

// $result now contains the decoded JSON response

Exception Handling

Guardian throws various exceptions to help you manage rate limiting and error scenarios. Here's how to handle them:

RulePreventsExecutionException

In our example above, we're hitting an API endpoint and getting some JSON back. However, our rules are rate-limiting our ability to get the data to only 6 requests per minute.

If we exceed this limit, Guardian will throw a RulePreventsExecutionException, unless you specify false as a second argument in the send method, in which case it'll return null.

Here's how to catch this exception:

use Midnite81\Guardian\Exceptions\RulePreventsExecutionException;

try {
    $result = $guardian->send(function () {
        $client = new Client();
        $response = $client->get('https://jsonplaceholder.typicode.com/posts/1');
        return json_decode($response->getBody()->getContents(), true);
    });
} catch (RulePreventsExecutionException $e) { 
    return "Rate limit exceeded: " . $e->getMessage();
}

RateLimitExceededException

There may be times when you set rules to prevent going over your rate limiting quota, but the server says you've already reached a limit. To prevent continued spamming of an API, we can throw a RateLimitExceededException in the callback. Guardian will then prevent any further API calls until the expiry time has passed.

use Midnite81\Guardian\Exceptions\RateLimitExceededException;
use GuzzleHttp\Exception\ClientException;

$guardian->send(function () use ($httpClient) {
    try {
        $response = $httpClient->get('https://api.example.com/endpoint');
        return $response->getBody()->getContents();
    } catch (ClientException $e) {
        if ($e->getResponse()->getStatusCode() === 429) {
            $retryAfter = $e->getResponse()->getHeaderLine('Retry-After');
            throw new RateLimitExceededException($retryAfter, 'Rate limit exceeded by external API');
        }
        // Handle other client exceptions...
        throw $e;
    }
});

Store Exceptions

Guardian uses various storage mechanisms to keep track of rate limits and other data. Each storage mechanism can throw its own type of exception, all of which extend the base StoreException. Here are the specific store exceptions:

  • DatabaseStoreException: Thrown when there's an issue with database operations.

  • FileStoreException: Thrown when there's a problem with file-based storage operations.

  • RedisStoreException: Thrown when there's an issue with Redis operations.

You can catch these exceptions individually or catch the base StoreException to handle all storage-related issues:

use Midnite81\Guardian\Exceptions\Store\StoreException;
use Midnite81\Guardian\Exceptions\Store\DatabaseStoreException;
use Midnite81\Guardian\Exceptions\Store\FileStoreException;
use Midnite81\Guardian\Exceptions\Store\RedisStoreException;

try {
    $result = $guardian->send(function () {
        // Your API call here
    });
} catch (DatabaseStoreException $e) {
    // Handle database-specific storage issues
} catch (FileStoreException $e) {
    // Handle file-specific storage issues
} catch (RedisStoreException $e) {
    // Handle Redis-specific storage issues
} catch (StoreException $e) {
    // Handle any other storage-related issues
}

Other Exceptions

You should be prepared to catch any other exceptions that the callback may throw. A general catch (Exception $e) is a good approach to prevent your application from potentially displaying an internal server error to users.

use Exception;

try {
    $result = $guardian->send(function () {
        // Your API call here
    });
} catch (RulePreventsExecutionException $e) {
    // Handle rate limiting
} catch (RateLimitExceededException $e) {
    // Handle external API rate limiting
} catch (StoreException $e) {
    // Handle storage issues
} catch (Exception $e) {
    // Handle any other unexpected exceptions
    return "An unexpected error occurred: " . $e->getMessage();
}

By handling these exceptions, you can gracefully manage rate limiting, storage issues, and other potential errors in your application.

Last updated