Skip to content
advanced Phase 88 · Integration Advanced

API Integration

API integration - REST/SOAP clients, API versioning, authentication, rate limiting

45m
0 problems
Topic Progress 0%

REST Client Architecture

REST Client Wrapper

namespace Vendor\Integration\Api\Client;

interface RestClientInterface
{
    public function get(string $endpoint, array $params = []): ResponseInterface;
    public function post(string $endpoint, array $data = []): ResponseInterface;
    public function put(string $endpoint, array $data = []): ResponseInterface;
    public function delete(string $endpoint): ResponseInterface;
}

Implementation

namespace Vendor\Integration\Api\Client;

class RestClient implements RestClientInterface
{
    private HttpClientInterface $httpClient;
    private AuthInterface $auth;
    private RateLimiterInterface $rateLimiter;
    private RetryHandler $retryHandler;
    private string $baseUrl;

    public function __construct(
        HttpClientInterface $httpClient,
        AuthInterface $auth,
        RateLimiterInterface $rateLimiter,
        RetryHandler $retryHandler,
        string $baseUrl
    ) {
        $this->httpClient = $httpClient;
        $this->auth = $auth;
        $this->rateLimiter = $rateLimiter;
        $this->retryHandler = $retryHandler;
        $this->baseUrl = rtrim($baseUrl, '/');
    }

    public function get(string $endpoint, array $params = []): ResponseInterface
    {
        return $this->request('GET', $endpoint, $params);
    }

    public function post(string $endpoint, array $data = []): ResponseInterface
    {
        return $this->request('POST', $endpoint, $data);
    }

    private function request(string $method, string $endpoint, array $data = []): ResponseInterface
    {
        $url = $this->baseUrl . '/' . ltrim($endpoint, '/');

        return $this->retryHandler->executeWithRetry(function () use ($method, $url, $data) {
            $this->rateLimiter->wait();

            $headers = $this->auth->getHeaders();
            $headers['Content-Type'] = 'application/json';

            return $this->httpClient->request($method, $url, [
                'headers' => $headers,
                'body' => $data ? json_encode($data) : null,
            ]);
        });
    }
}

API Versioning

Version Strategy

namespace Vendor\Integration\Api\Version;

class ApiVersionManager
{
    private string $currentVersion = 'v2';
    private array $versionMap = [
        'v1' => 'https://api.erp.com/v1/',
        'v2' => 'https://api.erp.com/v2/',
    ];

    public function getUrl(string $version = null): string
    {
        $version = $version ?? $this->currentVersion;
        return $this->versionMap[$version];
    }

    public function getDeprecated(): array
    {
        return array_filter(
            $this->versionMap,
            fn($v, $k) => $k !== $this->currentVersion,
            ARRAY_FILTER_USE_BOTH
        );
    }
}

Versioned Client

namespace Vendor\Integration\Api\Client;

class VersionedClient
{
    private RestClientFactory $clientFactory;
    private ApiVersionManager $versionManager;

    public function getClient(string $version = null): RestClientInterface
    {
        $url = $this->versionManager->getUrl($version);
        return $this->clientFactory->create($url);
    }
}

Response Version Handling

namespace Vendor\Integration\Api\Transform;

class VersionTransformer
{
    private array $transformers = [
        'v1' => V1Transformer::class,
        'v2' => V2Transformer::class,
    ];

    public function transform(string $version, array $response): array
    {
        $transformer = $this->transformers[$version] ?? null;
        if ($transformer) {
            return (new $transformer())->transform($response);
        }
        return $response;
    }
}

Authentication Patterns

OAuth2 Authentication

namespace Vendor\Integration\Api\Auth;

class OAuth2Auth implements AuthInterface
{
    private RestClientInterface $httpClient;
    private string $clientId;
    private string $clientSecret;
    private string $tokenUrl;
    private ?string $accessToken = null;
    private ?\DateTime $tokenExpiry = null;

    public function getHeaders(): array
    {
        if ($this->isTokenExpired()) {
            $this->refreshToken();
        }

        return [
            'Authorization' => 'Bearer ' . $this->accessToken,
        ];
    }

    private function refreshToken(): void
    {
        $response = $this->httpClient->post($this->tokenUrl, [
            'grant_type' => 'client_credentials',
            'client_id' => $this->clientId,
            'client_secret' => $this->clientSecret,
        ]);

        $data = json_decode($response->getBody(), true);
        $this->accessToken = $data['access_token'];
        $this->tokenExpiry = (new \DateTime())->modify("+{$data['expires_in']} seconds");
    }

    private function isTokenExpired(): bool
    {
        return $this->tokenExpiry === null || $this->tokenExpiry <= new \DateTime();
    }
}

API Key Authentication

namespace Vendor\Integration\Api\Auth;

class ApiKeyAuth implements AuthInterface
{
    private string $apiKey;
    private string $headerName = 'X-API-Key';

    public function getHeaders(): array
    {
        return [
            $this->headerName => $this->apiKey,
        ];
    }
}

Rate Limiting

Rate Limiter

namespace Vendor\Integration\Api\RateLimit;

class RateLimiter
{
    private int $maxRequestsPerSecond = 10;
    private int $currentRequests = 0;
    private \DateTime $windowStart;

    public function wait(): void
    {
        $now = new \DateTime();

        // Reset window every second
        if ($now->getTimestamp() - $this->windowStart->getTimestamp() >= 1) {
            $this->currentRequests = 0;
            $this->windowStart = $now;
        }

        // Wait if rate limit exceeded
        while ($this->currentRequests >= $this->maxRequestsPerSecond) {
            usleep(100000); // 100ms
            $now = new \DateTime();
            if ($now->getTimestamp() - $this->windowStart->getTimestamp() >= 1) {
                $this->currentRequests = 0;
                $this->windowStart = $now;
            }
        }

        $this->currentRequests++;
    }
}

Adaptive Rate Limiter

namespace Vendor\Integration\Api\RateLimit;

class AdaptiveRateLimiter
{
    private int $currentLimit = 100;
    private int $minLimit = 10;
    private int $maxLimit = 200;

    public function handleResponse(ResponseInterface $response): void
    {
        $statusCode = $response->getStatusCode();
        $remaining = $response->getHeader('X-RateLimit-Remaining');

        if ($statusCode === 429) {
            // Slow down
            $this->currentLimit = max($this->minLimit, $this->currentLimit / 2);
        } elseif ($remaining && (int) $remaining > 20) {
            // Speed up
            $this->currentLimit = min($this->maxLimit, $this->currentLimit + 10);
        }
    }
}

Quiz

1. What is API versioning?

Question 1 options

2. Why use OAuth2 for API authentication?

Question 2 options

3. What does a rate limiter do?

Question 3 options

Flashcards

Question

What is API versioning?

Answer

Managing multiple API versions for backward compatibility

Question

How does OAuth2 work?

Answer

Client credentials → token request → Bearer token → refresh on expiry

Question

What is adaptive rate limiting?

Answer

Adjusting limits based on API response codes

Question

What is retry with backoff?

Answer

Increasing delays between failed request retries

Revision Notes

Key Takeaways

  • 1. REST clients wrap HTTP calls with auth and rate limiting
  • 2. API versioning enables backward compatibility
  • 3. OAuth2 provides secure token-based authentication
  • 4. Rate limiting prevents API abuse and overload
  • 5. Retry with backoff handles transient failures

Interview Tips

  • Explain API versioning strategies (URL, header, query)
  • Discuss OAuth2 flow for server-to-server auth
  • Describe adaptive rate limiting based on response codes
  • Talk about handling rate limit responses gracefully

Cheat Sheet

API Integration:
  RestClient → GET/POST/PUT/DELETE
  Auth → OAuth2 / API Key
  Rate Limit → requests/second
  Retry → exponential backoff

Versioning:
  URL: /v1/resource, /v2/resource
  Header: Accept-Version: v2
  Query: ?version=2

Rate Limiting:
  Window-based: N requests/second
  Adaptive: adjust based on response
  Token bucket: allow bursts