Weave 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

Api Client 2 Laravel Package

engeni/api-client-2

Engeni API Client 2 is a fluent, Guzzle-powered HTTP client for Laravel and PHP applications, simplifying request building, authentication, error handling, and JSON response parsing so you can integrate third-party services with clean, testable code.

View on GitHub
Deep Wiki
Context7

Getting Started

This is a private, single-consumer package (Bitbucket-hosted, zero dependents), so treat the first hour as reconnaissance rather than a normal composer require.

  1. Get access before you get code. Add the Bitbucket repo to composer.json and stash credentials in auth.json (or CI secrets), never in the committed manifest:

    {
      "repositories": [
        { "type": "vcs", "url": "https://bitbucket.org/engeni/api-client-2.git" }
      ],
      "require": { "engeni/api-client-2": "^2.0" }
    }
    

    For CI, either use a Bitbucket app password / access token or mirror the package into your own Satis/Private Packagist.

  2. Read four files in this order. They tell you more than the README will:

    • composer.json — PHP and illuminate/* constraints. This is the single biggest integration risk; a stale ^8.0 constraint will fight your Laravel 11 app.
    • src/*ServiceProvider.php — what gets bound into the container, whether config is publishable, whether a facade alias is registered.
    • config/*.php — base URL, credentials, timeout, retry, and any endpoint map.
    • src/ entry class (usually a Client or Api class) — the public method surface. Skim the tests, if any exist, for the real usage contract.
  3. Publish and wire config:

    php artisan vendor:publish --provider="Engeni\ApiClient\ApiClientServiceProvider"
    

    Then set the env vars the config file references (base URL, client ID/secret, and any tenant/company key). Run php artisan config:clear before assuming anything is broken.

  4. First use case: a read-only smoke test. Don't start with writes. Resolve the client from the container and hit the cheapest authenticated GET the API exposes (health/ping, or a single-resource find($id)), then dump() the raw response. This validates credentials, base URL, and the response envelope shape in one shot. Write that as a throwaway artisan command or tinker session and keep the output — you'll reference it constantly.

  5. Confirm the transport. Determine whether the package wraps Laravel's Http facade or talks to Guzzle directly. This decides your entire testing strategy (see Gotchas).

Implementation Patterns

Inject, don't resolve inline. Bind once and let the container hand it to you — constructor injection in a job or a thin wrapper service beats app(Client::class) scattered through controllers.

Wrap it in your own service layer. Given the package's age and lack of community, put a narrow interface between your domain and engeni/api-client-2:

interface BillingGateway {
    public function invoice(string $id): InvoiceData;
}

final class EngeniBillingGateway implements BillingGateway
{
    public function __construct(private \Engeni\ApiClient\Client $client) {}

    public function invoice(string $id): InvoiceData
    {
        return InvoiceData::from($this->client->invoices()->get($id));
    }
}

This is not ceremony — it's the only thing that makes the package swappable when V3 lands or the repo goes dark.

Hydrate into DTOs at the boundary. The client will hand back arrays or stdClass. Convert to your own value objects immediately so a shape change upstream surfaces in one place instead of 40 views.

Keep pagination in one helper. Most clients of this style expose either a cursor or page/per-page pair. Wrap iteration in a generator so callers never hand-roll while ($hasMore) loops, and so you can add a page cap that prevents runaway syncs.

Push writes onto the queue. Anything that creates/updates remote state should be a job with $tries, backoff(), and idempotency keys if the API supports them. Interactive requests that block on a third-party API are how you get 30-second page loads during the vendor's incidents.

Multi-tenant apps: register the client scoped, not singleton. If credentials or a tenant header vary per request, bind with ->scoped() (or build a per-tenant client via a factory) so Octane and queued workers don't leak one tenant's credentials into another's request.

Test with the transport's native fake. If it uses Http, Http::fake([...]) in feature tests is enough. If it uses raw Guzzle, inject a MockHandler through the client's constructor or a container binding, and record fixtures from the smoke test in step 4 above. Fixture-based tests are your regression net for upstream changes.

Log around the boundary. A single structured log line per outbound call — method, path (not full body), status, duration, correlation ID — pays for itself the first time the vendor asks "did you send it?"

Gotchas and Tips

  • No Packagist, no community. Zero stars and zero dependents means no Stack Overflow answers, no issue history to search, and no guarantee the repo outlives your project. Plan for a fork: vendor it, or mirror to your own registry so a Bitbucket permission change can't break your build.
  • Pin exactly, then upgrade deliberately. Use a tight constraint (2.x.y or a commit hash) and treat every bump as a code review, because there's no changelog discipline to rely on. Add a composer outdated check to CI so you notice drift.
  • Laravel/PHP constraint mismatch is the most likely install failure. If composer require refuses to resolve, don't --ignore-platform-reqs blindly; check whether the package's illuminate/support range actually covers your version, and prefer a fork with a widened constraint over a global ignore.
  • Http::fake() silently does nothing if the package uses Guzzle directly — your "mocked" tests will hit the real API. Verify by asserting a ConnectionException when you fake a 500. This is the #1 source of accidental live calls from test suites.
  • Token caching and expiry. If the client authenticates, it likely caches a token — sometimes in Laravel's cache with a TTL derived from expires_in. Clock skew, config:cache, and shared Redis across environments all bite here. Check whether the cache key is namespaced per credential set; if not, add your own prefix or you'll get cross-environment 401s.
  • Wrap every call in an explicit timeout. Default Guzzle timeouts are effectively unlimited; a hung vendor endpoint will exhaust your PHP-FPM workers. Set connect and total timeouts in config and confirm they're actually passed through.
  • Exception taxonomy. Determine early whether failures throw, return null, or return ['error' => ...]. Normalize to your own exception types in the wrapper so callers have one thing to catch and you have one place to add Sentry breadcrumbs.
  • Retries need idempotency. Safe on GET, dangerous on POST. If the package has built-in retry config, disable it for write paths unless the API accepts an idempotency key.
  • Redact secrets in logs and exception reports. Auth headers and any PII in payloads will land in your logs and your error tracker by default. Add a scrubber before you ship.
  • Extension points to look for: a configurable Guzzle middleware stack or handler, container bindings you can extend(), and any Macroable trait on the client. A middleware hook is the cleanest place to inject correlation IDs, logging, and retry policy without forking.
  • Write characterization tests on day one. A dozen tests pinning the current behavior of the methods you actually use is cheap insurance; when the vendor ships a silent change, you'll know which of your call sites broke instead of discovering it in production.
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.
elmogy/larafast
xeviant/laravel-paystack
gorilladash/laravel-website-sdk
martialbe/flysystem-oss
chondal/3312client
jeroen-g/flickr
opscale-co/nova-genbi
itsjustvita/laravel-retention
tequilarapido/trackit
gregsphotography/site-builder-filament
cabaoctavio/laravel-query-log
gregsphotography/site-builder-components
io-digital/clickatell
snowsoft/laravel-admin
oxygen/auth
relicsoftware/barakah-payment-gateway
tobya/work-with-composer
equipc/laravel-email-validator
equipc/laravel-email-database-log
adedsec/nova-translated-labels