How do you properly handle calls to external APIs?
@kiwi0134 Unfortunately, there isn’t one single way to handle all third party APIs. Different API vendors interpret things like the HTTP spec differently… and sometimes not at all. Some will use rate limiting; others won’t. Those who do implement rate limiting, will do so in different ways, and so on.
You’re on the right track in that you’re identifying the “characteristics” of the API you’re interacting with. The important thing is to create a “layer” between the API and your application. You should have some sort of mapping layer that does the API requests, but then converts the data to instances of classes within your application that models whatever entities you’re working with. If you support multiple APIs for the same entity, then you’d just have multiple mapping layers that all convert the API-specific representations, to the canonical instance in your application.
Think of payment gateways for example. Different payment gateways will use different terminology for things like a “charge”. So you could create a Charge class in your application:
use App\Enums\ChargeProvider;
use Money\Money;
class Charge
{
public readonly ChargeProvider $provider;
public readonly string $providerId;
public readonly Money $amount;
public function __construct(ChargeProvider $provider, string $id, Money $amount)
{
$this->provider = $provider;
$this->providerId = $providerId;
$this->amount = $amount;
}
}
When you then process a charge with say, Stripe, you’d have some class that converts a Stripe charge object to an instance of your application’s Charge class. Same if you process a charge with an alternative gateway such as PayPal. Your application works with its own representation of things, but you have another layer that does the actual interaction with the external APIs and converts them to your application’s internal representation.
For things like rate limiting or API errors, you can look at architecture patterns such as “circuit breakers”. This is basically just wrapping an interaction and terminating it if a precondition fails, such as an error from the API or the request exceeds a predefined timeout threshold.
For things like receiving a 201 Created response versus a 204 No Content response, this is something you need to handle. Usually you receive a 201 Created response from endpoints where you’re creating entirely new resources, so I’m not sure what endpoint you’d hit to create a resource that it would then respond with, “Oh, here’s a No Content response because we already have that resource”. Either way, if you do receive a 204 No Content response then you’re going to need some logic that then loads the existing resource and returns that to your application in order to construct a class representing that entity. But if you’re expecting a new resource to be created (rather than a response saying it already exists), then that may be something you need to model in your application.