Weave Code
Code Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Saloon Laravel Package

saloonphp/saloon

Saloon is a PHP HTTP client framework for building API integrations. Define connectors and requests, handle authentication, retries, and responses, and test easily with fakes and mocking. Works great in Laravel or any PHP app.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Installation:

    composer require saloonphp/saloon
    

    Add to config/app.php under providers:

    Saloon\Laravel\SaloonServiceProvider::class,
    
  2. First Request: Define a request class (e.g., app/Http/Saloon/Requests/GetUser.php):

    use Saloon\Contracts\SaloonRequest;
    use Saloon\Enums\HttpMethod;
    use Saloon\Traits\BodyParameters;
    
    class GetUser extends SaloonRequest
    {
        public $endpoint = 'users/1';
        public $method = HttpMethod::GET;
        protected $baseUri = 'https://api.example.com';
    }
    
  3. Send the Request:

    use Saloon\Saloon;
    
    $response = Saloon::send(new GetUser());
    $data = $response->json();
    
  4. Key Files to Explore:

    • config/saloon.php (global config)
    • app/Http/Saloon/ (request classes)
    • tests/Feature/Saloon/ (test patterns)

Implementation Patterns

1. Request-Response Workflow

Pattern: Use dedicated request classes for each API endpoint.

class CreateOrder extends SaloonRequest
{
    public $endpoint = 'orders';
    public $method = HttpMethod::POST;
    protected $baseUri = 'https://api.merchant.com';

    public function resolveEndpoint(): string
    {
        return $this->endpoint . '?' . http_build_query($this->query);
    }

    public function defaultBody(): array
    {
        return [
            'amount' => $this->amount,
            'currency' => $this->currency,
        ];
    }
}

Usage:

$order = Saloon::send(new CreateOrder(['amount' => 100, 'currency' => 'USD']));

2. Authentication

OAuth2 Example:

use Saloon\Auth\OAuth2;

class AuthenticatedRequest extends SaloonRequest
{
    protected $auth = OAuth2::class;
    protected $authConfig = [
        'client_id' => env('OAUTH_CLIENT_ID'),
        'client_secret' => env('OAUTH_SECRET'),
        'token_url' => 'https://oauth.example.com/token',
    ];
}

Bearer Token:

use Saloon\Auth\BearerToken;

class BearerRequest extends SaloonRequest
{
    protected $auth = BearerToken::class;
    protected $authConfig = ['token' => $this->token];
}

3. Middleware Stack

Global Middleware (in config/saloon.php):

'middleware' => [
    \Saloon\Http\Middleware\AddDefaultHeaders::class,
    \App\Http\Saloon\Middleware\LogRequests::class,
],

Request-Specific Middleware:

class PaginatedRequest extends SaloonRequest
{
    protected $middleware = [
        \Saloon\Http\Middleware\Pagination::class,
    ];
}

4. Response Handling

Type-Safe Parsing:

use Saloon\Decorators\ResponseDecorator;

class UserResponse extends ResponseDecorator
{
    public function user(): array
    {
        return $this->json('data.user');
    }
}

Usage:

$response = Saloon::send(new GetUser());
$user = $response->user(); // Auto-parsed

5. Mocking for Tests

Fixture-Based Mocking:

// tests/Feature/Saloon/GetUserTest.php
public function test_get_user()
{
    $this->mock(Saloon::class, GetUser::class)
        ->shouldReceive('send')
        ->once()
        ->andReturn(new GetUserResponse(['name' => 'John']));

    $response = Saloon::send(new GetUser());
    $this->assertEquals('John', $response->json('name'));
}

6. Error Handling

Custom Exceptions:

use Saloon\Exceptions\HttpException;
use Saloon\Exceptions\SaloonException;

class PaymentFailedException extends SaloonException {}

class PaymentRequest extends SaloonRequest
{
    public function handleException(HttpException $exception): void
    {
        if ($exception->response->status == 402) {
            throw new PaymentFailedException($exception->getMessage());
        }
    }
}

7. Pagination

Auto-Pagination:

use Saloon\Traits\Pagination;

class ListUsers extends SaloonRequest
{
    use Pagination;

    public $endpoint = 'users';
    public $method = HttpMethod::GET;
    protected $paginateKey = 'data';
    protected $perPage = 20;
}

Usage:

$users = Saloon::send(new ListUsers())->all();

8. Macros (v3.15+)

Dynamic Request Modification:

GetUser::macro('withRole', function ($role) {
    $this->query['role'] = $role;
    return $this;
});

// Usage:
$response = Saloon::send((new GetUser())->withRole('admin'));

Gotchas and Tips

Common Pitfalls

  1. Base URI Overrides (v4.0+)

    • Absolute URLs in endpoint now override baseUri by default (security fix for SSRF).
    • Fix: Use relative paths or opt-in to overrides:
      $request->withBaseUriOverride(true);
      
  2. Fixture Path Traversal (v4.0+)

    • Fixtures must use safe paths (e.g., database/fixtures/).
    • Fix: Avoid ../ in fixture paths; use base_path('fixtures/...').
  3. Authenticator Injection (CVE-2026-33942)

    • Avoid dynamic AccessTokenAuthenticator instantiation with user input.
    • Fix: Use static configs or NullAuthenticator for unauthenticated requests.
  4. Case-Sensitive Headers

    • Content-Type: json may fail; use lowercase (Content-Type: JSON).
    • Fix: Normalize headers in middleware:
      $request->withHeaders(['Content-Type' => 'application/json']);
      
  5. Deprecated Methods

    • sendAndRetry() and auth() methods are deprecated.
    • Fix: Use Saloon::send() with middleware or defaultAuth.

Debugging Tips

  1. Pretty-Print Responses (v3.6.3+)

    $response->prettyPrint(); // Dumps formatted JSON/XML
    
  2. Debug Middleware Add a debug middleware to inspect requests/responses:

    class DebugMiddleware implements Middleware
    {
        public function handle(SaloonRequest $request): void
        {
            \Log::debug('Request:', [
                'url' => $request->resolveEndpoint(),
                'body' => $request->body(),
                'headers' => $request->headers(),
            ]);
        }
    }
    
  3. Assertion Failures

    • Use assertSent() to verify requests in tests:
      $this->assertSent(GetUser::class, function (GetUser $request) {
          return $request->userId === 1;
      });
      
  4. Retry Logic

    • Customize retries in config/saloon.php:
      'retry' => [
          'max_attempts' => 3,
          'delay' => 1000,
          'exceptions' => [
              \Saloon\Exceptions\HttpException::class,
          ],
      ],
      

Extension Points

  1. Custom Response Decorators Extend ResponseDecorator to add domain-specific methods:

    class StripeResponse extends ResponseDecorator
    {
        public function paymentIntent(): array
        {
            return $this->json('data.object');
        }
    }
    
  2. PSR-18 Client Integration Replace Guzzle with a custom client:

    use Saloon\Contracts\Client;
    use Saloon\Http\Clients\Psr18Client;
    
    class CustomClient implements Client
    {
        public function send(SaloonRequest $request): Response
        {
            $psrRequest = new \Psr\Http\Message\Request(
                $request->method,
                $request->resolveEndpoint(),
                $request->headers(),
                $request->body()
            );
            $response = $this->psrClient->send($psrRequest);
            return new \Saloon\Http\Responses\Response($response);
        }
    }
    
  3. Dynamic Endpoints Use resolveEndpoint() for runtime URL generation:

    public function resolveEndpoint(): string
    {
        return "users/{$this->userId}/orders";
    }
    
  4. Fixture Merging (v3.11+) Override mock responses dynamically

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
hamzi/corewatch
minionfactory/raw-hydrator
hexters/coinpayment
rjcodes/rjcms
act-training/laravel-permissions-manager
alimarchal/laravel-chart-of-accounts
babenkoivan/elastic-scout-driver
mkwebdesign/filament-watchdog-v5
renatomarinho/laravel-page-speed
zedmagdy/filament-business-hours
renatovdemoura/blade-elements-ui
devgeek/beacon-admin
benjamin-rqt/data-watcher-bundle
atriumphp/atrium
sandermuller/package-boost-laravel
sandermuller/boost-skills
redaxo/core
yusufgenc/filament-api-forge
l3aro/rating-star-for-filament
leek/filament-subtenant-scope