insideapps/api-proxy

Provides basics api proxy for symfony http client based

2.1.1 2025-05-26 11:56 UTC

This package is auto-updated.

Last update: 2025-05-26 10:07:40 UTC


README

PHP 8.1+ Symfony 6.4+ License MIT

A lightweight and flexible proxy layer for making API requests using Symfony's HTTP client components.

Requirements

  • PHP >= 8.1
  • Symfony/HttpClient (6.4+)
  • Symfony/Cache (6.4+)
  • Symfony/DotEnv (6.4+)
  • Symfony/Serializer (6.4+)

Installation

composer require insideapps/api-proxy

Features

  • Bearer token authentication
  • Automatic token refresh
  • Request caching
  • Support for all common HTTP methods (GET, POST, PUT, DELETE)
  • Configurable timeout and retry options

Usage

Basic Setup

Bearer Token Authentication

use InsideApps\ApiProxy\Factory\ProxyFactory;

// Create a client with Bearer token authentication
$apiClient = ProxyFactory::bearerClient(
    baseUrl: 'https://api.example.com/',
    authUrl: 'https://api.example.com/auth',
    credentialItems: [
        'username' => 'your_username',
        'password' => 'your_password'
    ]
);

// Make a simple GET request
$response = $apiClient->get('users/123');

// Access response data
$userData = $response->toArray();

Caching Responses

Enable caching for GET requests to improve performance:

// Parameters:
// 1. Endpoint
// 2. Enable cache (boolean)
// 3. Cache lifetime in seconds (default: 3600)
$response = $apiClient->get('products', true, 60);

HTTP Methods

The client supports all common HTTP methods:

// GET request
$response = $apiClient->get('users');

// POST request with data
$response = $apiClient->post('users', [
    'name' => 'John Doe',
    'email' => 'john@example.com'
]);

// PUT request
$response = $apiClient->put('users/123', [
    'name' => 'Updated Name'
]);

// DELETE request
$response = $apiClient->delete('users/123');

Advanced Configuration

You can configure additional options when creating the client:

$apiClient = ProxyFactory::bearerClient(
    baseUrl: 'https://api.example.com/',
    authUrl: 'https://api.example.com/auth',
    credentialItems: [
        'username' => 'your_username',
        'password' => 'your_password'
    ],
    options: [
        'timeout' => 30,
        'max_retries' => 3
    ]
);

Symfony Integration

Environment Variables

Add the required environment variables to your .env file:

# .env
# Common API settings
API_BASE_URL=https://api.example.com
API_AUTH_URL=https://api.example.com/auth
API_USERNAME=your_username
API_PASSWORD=your_password

# Multiple API endpoints can be configured
USERS_API_BASE_URL=https://users-api.example.com
USERS_API_AUTH_URL=https://users-api.example.com/auth
USERS_API_USERNAME=users_username
USERS_API_PASSWORD=users_password

PRODUCTS_API_BASE_URL=https://products-api.example.com
PRODUCTS_API_AUTH_URL=https://products-api.example.com/auth
PRODUCTS_API_USERNAME=products_username
PRODUCTS_API_PASSWORD=products_password

Service Configuration

You can easily integrate the API Proxy into your Symfony application by defining repositories as services in your services.yaml file:

# config/services.yaml
parameters:
    # Define common API parameters
    app.api.base_url: '%env(API_BASE_URL)%'
    app.api.auth_url: '%env(API_AUTH_URL)%'
    app.api.username: '%env(API_USERNAME)%'
    app.api.password: '%env(API_PASSWORD)%'
    
    # Define specific API parameters for different endpoints
    app.users_api.base_url: '%env(USERS_API_BASE_URL)%'
    app.users_api.auth_url: '%env(USERS_API_AUTH_URL)%'
    app.users_api.username: '%env(USERS_API_USERNAME)%'
    app.users_api.password: '%env(USERS_API_PASSWORD)%'
    
    app.products_api.base_url: '%env(PRODUCTS_API_BASE_URL)%'
    app.products_api.auth_url: '%env(PRODUCTS_API_AUTH_URL)%'
    app.products_api.username: '%env(PRODUCTS_API_USERNAME)%'
    app.products_api.password: '%env(PRODUCTS_API_PASSWORD)%'

services:
    # Factory service
    InsideApps\ApiProxy\Factory\ProxyFactory:
        public: true
        
    # Abstract repository service
    App\Repository\AbstractApiRepository:
        abstract: true
        
    # User repository service
    App\Repository\UserRepository:
        arguments:
            $baseUrl: '%app.users_api.base_url%'
            $authUrl: '%app.users_api.auth_url%'
            $username: '%app.users_api.username%'
            $password: '%app.users_api.password%'
            $serializer: '@serializer'
            $endpoint: 'users'
            
    # Product repository service
    App\Repository\ProductRepository:
        arguments:
            $baseUrl: '%app.products_api.base_url%'
            $authUrl: '%app.products_api.auth_url%'
            $username: '%app.products_api.username%'
            $password: '%app.products_api.password%'
            $serializer: '@serializer'
            $endpoint: 'products'

Creating an Abstract Repository

Here's an example of creating an abstract repository class that uses the API Proxy with Symfony's Serializer component:

<?php

namespace App\Repository;

use InsideApps\ApiProxy\ApiProxy;
use InsideApps\ApiProxy\Factory\ProxyFactory;
use Symfony\Component\Serializer\SerializerInterface;

/**
 * Abstract API repository with common functionality
 */
abstract class AbstractApiRepository
{
    protected ApiProxy $apiClient;
    protected SerializerInterface $serializer;
    protected string $endpoint;
    protected string $entityClass;
    
    /**
     * Create a new repository instance with API client
     */
    public function __construct(
        string $baseUrl,
        string $authUrl,
        string $username,
        string $password,
        SerializerInterface $serializer,
        string $endpoint,
        string $entityClass = null
    ) {
        // Create the API client in the constructor
        $this->apiClient = ProxyFactory::bearerClient(
            baseUrl: $baseUrl,
            authUrl: $authUrl,
            credentialItems: [
                'username' => $username,
                'password' => $password
            ]
        );
        
        $this->serializer = $serializer;
        $this->endpoint = $endpoint;
        $this->entityClass = $entityClass ?? 'App\\Entity\\' . ucfirst(substr($endpoint, -1) === 's' ? substr($endpoint, 0, -1) : $endpoint);
    }
    
    /**
     * Get all resources
     */
    public function findAll(bool $useCache = true, int $cacheLifetime = 3600): array
    {
        $response = $this->apiClient->get($this->endpoint, $useCache, $cacheLifetime);
        return $this->serializer->deserialize($response, $this->entityClass . '[]', 'json');
    }
    
    /**
     * Find a resource by ID
     */
    public function find(int $id, bool $useCache = true, int $cacheLifetime = 3600): ?object
    {
        $response = $this->apiClient->get($this->endpoint . '/' . $id, $useCache, $cacheLifetime);
        return $this->serializer->deserialize($response, $this->entityClass, 'json');
    }
    
    /**
     * Create a new resource
     */
    public function create(array $data): object
    {
        $response = $this->apiClient->post($this->endpoint, $data);
        return $this->serializer->deserialize($response, $this->entityClass, 'json');
    }
    
    /**
     * Update an existing resource
     */
    public function update(int $id, array $data): object
    {
        $response = $this->apiClient->put($this->endpoint . '/' . $id, $data);
        return $this->serializer->deserialize($response, $this->entityClass, 'json');
    }
    
    /**
     * Delete a resource
     */
    public function delete(int $id): bool
    {
        $this->apiClient->delete($this->endpoint . '/' . $id);
        return true;
    }
}

Implementing a Concrete Repository

<?php

namespace App\Repository;

/**
 * User API repository
 */
class UserRepository extends AbstractApiRepository
{
    // You can override methods or add custom ones specific to users
    
    /**
     * Find users by role
     */
    public function findByRole(string $role, bool $useCache = true): array
    {
        $response = $this->apiClient->get($this->endpoint . '/role/' . $role, $useCache);
        return $this->serializer->deserialize($response, $this->entityClass . '[]', 'json');
    }
}

Using the Repository in a Controller

<?php

namespace App\Controller;

use App\Repository\UserRepository;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Annotation\Route;

class UserController extends AbstractController
{
    private UserRepository $userRepository;

    public function __construct(UserRepository $userRepository)
    {
        $this->userRepository = $userRepository;
    }

    #[Route('/api/users', name: 'api_users_index', methods: ['GET'])]
    public function index(): JsonResponse
    {
        $users = $this->userRepository->findAll();
        return $this->json($users);
    }

    #[Route('/api/users/{id}', name: 'api_users_show', methods: ['GET'])]
    public function show(int $id): JsonResponse
    {
        $user = $this->userRepository->find($id);
        return $this->json($user);
    }
    
    #[Route('/api/users', name: 'api_users_create', methods: ['POST'])]
    public function create(Request $request): JsonResponse
    {
        $data = json_decode($request->getContent(), true);
        $user = $this->userRepository->create($data);
        return $this->json($user, 201);
    }
    
    #[Route('/api/users/role/{role}', name: 'api_users_by_role', methods: ['GET'])]
    public function findByRole(string $role): JsonResponse
    {
        $users = $this->userRepository->findByRole($role);
        return $this->json($users);
    }
}

License

MIT