PHPackages                             usamamuneerchaudhary/laraclient - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [HTTP &amp; Networking](/categories/http)
4. /
5. usamamuneerchaudhary/laraclient

ActiveLibrary[HTTP &amp; Networking](/categories/http)

usamamuneerchaudhary/laraclient
===============================

Configure every third-party API your Laravel app talks to in one file, then see every call in one dashboard.

2.2(3w ago)50163MITPHPPHP ^8.3CI passing

Since Mar 26Pushed 3w ago2 watchersCompare

[ Source](https://github.com/usamamuneerchaudhary/laraclient)[ Packagist](https://packagist.org/packages/usamamuneerchaudhary/laraclient)[ GitHub Sponsors](https://github.com/usamamuneerchaudhary)[ RSS](/packages/usamamuneerchaudhary-laraclient/feed)WikiDiscussions main Synced 2w ago

READMEChangelog (5)Dependencies (15)Versions (8)Used By (0)

LaraClient
==========

[](#laraclient)

Configure every third-party API your Laravel app talks to in one file, then see every call in one dashboard.

[![Latest Version on Packagist](https://camo.githubusercontent.com/72f620e133dbbbe898a859aa088463f5cc7946e49572f50bc375ed0450f4b88f/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f7573616d616d756e6565726368617564686172792f6c617261636c69656e743f7374796c653d666c61742d737175617265)](https://packagist.org/packages/usamamuneerchaudhary/laraclient)[![Total Downloads](https://camo.githubusercontent.com/0f144ecb7b5073365db23289e70404f31409dca11ef29dee36af89c439b59284/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f7573616d616d756e6565726368617564686172792f6c617261636c69656e743f7374796c653d666c61742d737175617265)](https://packagist.org/packages/usamamuneerchaudhary/laraclient)[![License](https://camo.githubusercontent.com/8eab6b7ecd42dc9981809ecec2101522fe3ebd5d7180a427ac26348d479617ca/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f6c2f7573616d616d756e6565726368617564686172792f6c617261636c69656e743f7374796c653d666c61742d737175617265)](LICENSE.md)

[![laraclient-1400.png](public/images/laraclient-1400.png)](public/images/laraclient-1400.png)

```
use Usamamuneerchaudhary\LaraClient\Facades\LaraClient;

$weather = LaraClient::connection('weatherapi')->get('current.json', ['q' => 'london']);
$countries = LaraClient::connection('geodb')->get('countries');

$weather->json('current.temp_c');
```

Ten APIs, ten credential schemes, ten sets of rate limits, declared once in `config/lara_client.php`instead of scattered across ten service classes. Retries, throttling, caching, circuit breaking and redacted request logging come with it.

---

Requirements
------------

[](#requirements)

PHP8.3, 8.4, 8.5Laravel13.xGuzzle7.9+Running Laravel 10–12? Stay on LaraClient 1.x, or see [UPGRADE.md](UPGRADE.md).

Installation
------------

[](#installation)

```
composer require usamamuneerchaudhary/laraclient
php artisan vendor:publish --tag=laraclient-config
php artisan migrate
```

The service provider and `LaraClient` alias are registered automatically, no `app.php` edit needed.

---

Configuring connections
-----------------------

[](#configuring-connections)

Each connection declares only what makes it different; everything else falls back to the `defaults`block.

```
'defaults' => [
    'timeout' => 30,
    'retry' => ['enabled' => true, 'times' => 3],
    'circuit_breaker' => ['enabled' => true],
],

'connections' => [

    'github' => [
        'base_uri' => 'https://api.github.com/',
        'auth' => ['driver' => 'bearer', 'token' => env('GITHUB_TOKEN')],
        'pagination' => ['strategy' => 'link_header'],
    ],

    'weatherapi' => [
        'base_uri' => 'https://weatherapi-com.p.rapidapi.com/',
        'auth' => ['driver' => 'header', 'name' => 'X-RapidAPI-Key', 'value' => env('RAPIDAPI_KEY')],
        'cache' => ['enabled' => true, 'ttl' => 600],
    ],

    'salesforce' => [
        'base_uri' => env('SALESFORCE_BASE_URI'),
        'auth' => [
            'driver' => 'oauth2',
            'token_url' => env('SALESFORCE_TOKEN_URL'),
            'client_id' => env('SALESFORCE_CLIENT_ID'),
            'client_secret' => env('SALESFORCE_CLIENT_SECRET'),
        ],
        'rate_limit' => ['enabled' => true, 'limit' => 100, 'window' => 60],
    ],
],
```

### Authentication drivers

[](#authentication-drivers)

DriverUse for`bearer``Authorization: Bearer ``basic`HTTP basic credentials`header`A key in any header, `X-API-Key`, `X-RapidAPI-Key`, `apikey``query`A key as a query parameter`oauth2`Client credentials grant, with tokens cached and refreshed for you`none`Public APIsOAuth2 tokens are fetched on first use, cached until shortly before expiry, and re-fetched automatically on a 401. None of that appears in your application code.

Signing schemes of your own (HMAC, JWT assertions, mTLS headers) plug in without forking:

```
use Usamamuneerchaudhary\LaraClient\Auth\AuthFactory;

AuthFactory::extend('hmac', fn (array $config) => new HmacAuth($config['secret']));
```

---

Making requests
---------------

[](#making-requests)

```
LaraClient::connection('github')->get('users/{username}/repos', [
    'username' => 'usamamuneerchaudhary',   // fills the path placeholder
    'sort' => 'updated',                    // left over, so it becomes a query parameter
]);
```

Everything is fluent, and every method returns a clone, so a shared base request never leaks one caller's settings into another's.

```
LaraClient::connection('stripe')
    ->withHeader('Stripe-Version', '2024-06-20')
    ->timeout(5)
    ->retry(times: 4, baseDelayMs: 250)
    ->idempotent($job->uuid)
    ->throwOnError()
    ->post('charges', ['amount' => 500, 'currency' => 'gbp']);
```

**Full fluent API**MethodEffect`withHeaders()` / `withHeader()` / `accept()`Add headers`withQuery()`Add query parameters to every call`withToken()` / `withBasicAuth()` / `withAuth()` / `withoutAuth()`Override credentials`timeout()` / `connectTimeout()`Per-request timeouts`retry()` / `withoutRetrying()` / `retryUnsafeMethods()`Retry policy`cacheFor()` / `fresh()` / `withoutCache()`Response caching`throttle()` / `withoutThrottling()`Client-side rate limiting`withoutCircuitBreaker()` / `withoutLogging()`Opt out per call`idempotent()`Stamp an `Idempotency-Key``asJson()` / `asForm()` / `asMultipart()`Body encoding`sink()`Stream the response to a file`throwOnError()`Turn 4xx/5xx into exceptions`async()`Return a promise instead of a response### Responses

[](#responses)

```
$response->json();                  // whole payload as an array
$response->json('data.0.email');    // dot-addressed
$response->collect('data');         // Illuminate Collection
$response->object();
$response->body();

$response->ok();  $response->successful();  $response->failed();
$response->clientError();  $response->serverError();
$response->notFound();  $response->unauthorized();  $response->tooManyRequests();

$response->header('X-Request-Id');
$response->fromCache();
$response->durationMs();

$response->throw();                       // RequestException on 4xx/5xx
$response->onError(fn ($r) => report($r->json('error.message')));
```

`json()` decodes once and reuses the result, and `isJson()` tells you whether the body actually parsed, which is how you catch an HTML error page served with a 200.

---

Resilience
----------

[](#resilience)

### Retries

[](#retries)

Exponential backoff with full jitter, bounded attempts, and `Retry-After` honoured in both integer and HTTP-date form.

```
'retry' => [
    'times' => 3,
    'base_delay' => 200,   // 200ms -> 400ms -> 800ms, randomised
    'jitter' => true,
    'statuses' => [408, 425, 429, 500, 502, 503, 504],
    'methods' => ['GET', 'HEAD', 'PUT', 'DELETE', 'OPTIONS'],
],
```

Unsafe verbs are **not** retried by default, replaying a `POST` can charge a customer twice. Opt in with `retryUnsafeMethods()`, ideally alongside `idempotent()`.

### Rate limiting

[](#rate-limiting)

Throttling is scoped per connection and **throws instead of sleeping**, so a queued job can hand the work back to the queue rather than parking a worker:

```
public function handle(): void
{
    try {
        LaraClient::connection('salesforce')->get('accounts');
    } catch (RateLimitExceededException $e) {
        $this->release($e->retryAfter);
    }
}
```

Set `'on_limit' => 'wait'` if blocking is genuinely what you want.

### Circuit breaker

[](#circuit-breaker)

After N consecutive server errors the circuit opens and requests fail fast with `CircuitOpenException` instead of piling load onto an upstream that is already down. Once the cooldown elapses, the next request goes through as a probe: success closes the circuit, failure restarts the cooldown.

```
'circuit_breaker' => ['enabled' => true, 'failure_threshold' => 5, 'cooldown' => 60],
```

`CircuitOpened` and `CircuitClosed` events fire on each transition.

---

Caching
-------

[](#caching)

```
LaraClient::connection('weatherapi')->cacheFor(600)->get('current.json', ['q' => 'london']);
```

GET and HEAD only. `Cache-Control: max-age` from the provider wins when it is shorter than your TTL, `no-store` and `private` are respected, and entries with an `ETag` are revalidated with a conditional request when they go stale, a 304 costs one round trip and no payload.

On a tagged cache store you can flush one integration without touching the rest of the app:

```
Cache::tags('laraclient:weatherapi')->flush();
```

---

Concurrency
-----------

[](#concurrency)

Every request in a pool shares one curl multi handler, so they genuinely run in parallel.

```
use Usamamuneerchaudhary\LaraClient\Pool;

$results = LaraClient::connection('github')->pool(fn (Pool $pool) => [
    $pool->as('user')->get('user'),
    $pool->as('repos')->get('user/repos'),
    $pool->as('orgs')->get('user/orgs'),
]);

$results['user']->json('login');
```

A failed request appears in place as its exception rather than taking the batch down with it.

---

Pagination
----------

[](#pagination)

Declare the strategy once per connection, then stop thinking about it:

```
LaraClient::connection('github')
    ->paginate('user/repos')
    ->each(fn (array $repo) => Repo::sync($repo));
```

Backed by a `LazyCollection`, so memory stays flat across three pages or three thousand. Strategies: `page`, `offset`, `cursor` and `link_header` (RFC 8288). Use `->chunks()` to yield whole pages for batch inserts, and `->maxPages()` as a safety valve on endpoints that never stop.

---

Testing
-------

[](#testing)

```
LaraClient::fake([
    'github/user*' => LaraClient::response(['login' => 'usama']),
    'stripe/*'     => LaraClient::response(['error' => 'card_declined'], 402),
    '*'            => LaraClient::response([], 404),
]);

$this->post('/sync');

LaraClient::assertSentCount(1);
LaraClient::assertSent(fn ($request) => $request->isPost()
    && $request->urlIs('*charges')
    && $request->json('amount') === 500);
```

Patterns match either the full URL or the `connection/path` shorthand. An unmatched request returns an empty 200 rather than reaching the network, a fake that silently makes real calls is worse than no fake at all.

Pass a list to script a sequence, which is how you test retry paths:

```
LaraClient::fake(['*' => [
    LaraClient::response([], 503),
    LaraClient::response(['ok' => true]),
]]);
```

`LaraClient::failedConnection()` simulates a transport failure for exercising backoff and circuit breaker behaviour.

### Record and replay

[](#record-and-replay)

Hand-written fixtures drift from the API they describe. Record the real thing once, replay it forever:

```
LARACLIENT_RECORDER=record php artisan test --filter=GithubTest   # captures fixtures
LARACLIENT_RECORDER=replay php artisan test                       # hermetic, no network
```

Fixtures pass through the same redactor as the logs, so recording against a live account does not commit your API keys.

---

Observability
-------------

[](#observability)

### The dashboard

[](#the-dashboard)

`/laraclient/logs`, every outbound call, filterable by connection, endpoint and failure, with a latency trace strip across the top: one bar per call, height is latency, colour is outcome. A cluster of red bars is a failing window; a wall of tall bars is a slow upstream.

**The dashboard is gated.** It shows request and response bodies, so define who may see it:

```
// AppServiceProvider::boot()
Gate::define('viewLaraClient', fn ($user) => $user->isAdmin());
```

Without a definition it denies everyone outside `local`. Auth middleware is optional, add it to `dashboard.middleware` when you want Laravel to require a logged-in user first:

```
// config/lara_client.php
'dashboard' => [
    'middleware' => ['web', 'auth'], // or Filament, Sanctum, etc.
],
```

### Events

[](#events)

```
use Usamamuneerchaudhary\LaraClient\Events\{RequestSending, ResponseReceived, RequestFailed};
```

Plus global hooks, mirroring the shape Laravel 13 gave its own HTTP client:

```
LaraClient::beforeSending(function ($request, $method, $url, &$options) {
    $options['headers']['X-Request-Id'] = (string) Str::uuid();
});

LaraClient::afterResponse(fn ($response) => Log::debug($response->durationMs()));
```

### Pulse

[](#pulse)

If `laravel/pulse` is installed, per-connection latency and error counts are recorded automatically. Cache hits are excluded, counting them would flatter the numbers.

---

Artisan commands
----------------

[](#artisan-commands)

```
php artisan laraclient:check              # ping every connection; fails the build on a bad credential
php artisan laraclient:prune              # trim the logs table (schedule this daily)
php artisan laraclient:make github --spec=openapi.json
```

### Generating a typed client

[](#generating-a-typed-client)

`laraclient:make` turns an OpenAPI 3 document into a plain, readable PHP class you own:

```
class GithubClient
{
    /**
     * Fetch a single part of a widget
     *
     * @param  int  $widgetId  Numeric widget id
     */
    public function getWidgetPart(int $widgetId, string $partId, array $query = []): Response
    {
        return $this->request()->get('widgets/{widgetId}/parts/{partId}', [
            'widgetId' => $widgetId, 'partId' => $partId,
        ] + $query);
    }
}
```

Path parameters become typed, ordered arguments; `operationId` collisions are de-duplicated; `deprecated` operations are annotated. The fluent API stays reachable through `using()`:

```
$client->using(fn ($r) => $r->timeout(2)->cacheFor(300))->listWidgets();
```

It generates methods and parameters, **not** typed response DTOs, that is on the roadmap for 2.1.

---

Security
--------

[](#security)

Everything written to the logs table or a fixture passes through the redactor first: credential headers, sensitive body keys (recursively, including inside lists) and secrets carried in query strings. Widen the mask for your own field names:

```
'redact' => [
    'headers' => ['authorization', 'x-api-key', 'x-signature'],
    'body' => ['password', 'client_secret', 'card_number', 'national_id'],
],
```

If you are upgrading from 1.x, **read [UPGRADE.md](UPGRADE.md) before deploying**, v1 stored credentials in the logs table in plaintext, and there are steps to take beyond installing this release.

To report a vulnerability, email  rather than opening an issue.

---

Testing the package
-------------------

[](#testing-the-package)

```
composer test      # phpunit
composer analyse   # phpstan
composer lint      # pint
```

Credits
-------

[](#credits)

- [Usama Muneer](https://usamamuneer.me)
- [All Contributors](https://github.com/usamamuneerchaudhary/laraclient/graphs/contributors)

License
-------

[](#license)

MIT. See [LICENSE.md](LICENSE.md).

###  Health Score

51

—

FairBetter than 95% of packages

Maintenance95

Actively maintained with recent releases

Popularity19

Limited adoption so far

Community13

Small or concentrated contributor base

Maturity66

Established project with proven stability

 Bus Factor1

Top contributor holds 97.3% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~243 days

Recently: every ~304 days

Total

6

Last Release

24d ago

Major Versions

1.2 → 2.02026-07-25

PHP version history (2 changes)1.0PHP ^8.1

2.0PHP ^8.3

### Community

Maintainers

![](https://www.gravatar.com/avatar/adb088072d2e9f335a0c37e9e5481d8d3c582ccc4b4ca721850b1e32b3a480e8?d=identicon)[usamamuneerchaudhary](/maintainers/usamamuneerchaudhary)

---

Top Contributors

[![usamamuneerchaudhary](https://avatars.githubusercontent.com/u/24513380?v=4)](https://github.com/usamamuneerchaudhary "usamamuneerchaudhary (71 commits)")[![dependabot[bot]](https://avatars.githubusercontent.com/in/29110?v=4)](https://github.com/dependabot[bot] "dependabot[bot] (1 commits)")[![Oxicode](https://avatars.githubusercontent.com/u/1320709?v=4)](https://github.com/Oxicode "Oxicode (1 commits)")

---

Tags

api-clientapi-resthttp-clienthttp-requestslaravelpackagephprest-apihttpapilaravelGuzzleopenapiapi clientrate limitingcircuit breaker

###  Code Quality

TestsPHPUnit

Static AnalysisPHPStan

Code StyleLaravel Pint

Type Coverage Yes

### Embed Badge

![Health badge](/badges/usamamuneerchaudhary-laraclient/health.svg)

```
[![Health](https://phpackages.com/badges/usamamuneerchaudhary-laraclient/health.svg)](https://phpackages.com/packages/usamamuneerchaudhary-laraclient)
```

###  Alternatives

[laravel/ai

The official AI SDK for Laravel.

1.1k4.6M322](/packages/laravel-ai)[spatie/laravel-health

Monitor the health of a Laravel application

88212.7M188](/packages/spatie-laravel-health)[api-platform/laravel

API Platform support for Laravel

58190.1k21](/packages/api-platform-laravel)[psalm/plugin-laravel

Psalm plugin for Laravel

3345.4M354](/packages/psalm-plugin-laravel)[laravel/cashier

Laravel Cashier provides an expressive, fluent interface to Stripe's subscription billing services.

2.6k31.8M162](/packages/laravel-cashier)[illuminate/queue

The Illuminate Queue package.

20433.0M1.8k](/packages/illuminate-queue)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
