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

Guzzle Services Laravel Package

guzzlehttp/guzzle-services

Build SDK-style API clients with service descriptions: map named operations and parameters to HTTP requests and transform responses into result data, instead of hand-writing request serializers for every endpoint.

View on GitHub
Deep Wiki
Context7

Technical Evaluation

Architecture fit

  • The package sits at a specific layer: guzzlehttp/guzzle (transport) → guzzlehttp/command (command/handler pipeline) → guzzlehttp/guzzle-services (service description → request serialization → response model hydration). That maps cleanly onto the "SDK-style client for one external API" problem, which is different from the "make one HTTP call" problem Laravel's Http facade solves.
  • It is framework-agnostic by design: no service provider, no config publishing, no facade, no Laravel version constraints. In a Laravel codebase it becomes a container-bound singleton/factory plus a config file holding description arrays — roughly 100–150 lines of glue, all of it owned by us.
  • Strong fit when the integration surface is wide: 20+ named operations, uniform auth, consistent pagination/error semantics, and a desire to declare serialization in arrays instead of writing request builders per endpoint.
  • Weak fit when the surface is narrow (one or two endpoints — use Http::withToken()->get()), or when the team expects spec-first generation. There is no OpenAPI/Swagger importer in this package; descriptions are hand-authored arrays or produced by external codegen. That is the single biggest functional gap versus alternatives like Saloon or generated SDKs, and it moves the "keep in sync with the vendor's API" burden onto us.

Integration feasibility

  • Composer-resolvable, MIT, actively released, no Laravel coupling, so nothing structurally blocks adoption.
  • The real gate is the dependency major. The 2.0 line requires guzzlehttp/guzzle ^8, psr7 ^3, promises ^3, command ^2, and PHP >=7.4,<8.7. Laravel's Illuminate\Http\Client is built on Guzzle 7, so any app using the HTTP client (i.e., almost every app) cannot take 2.0 without a framework-wide Guzzle 7 → 8 uplift. Plan on the ^1.7 line today; it supports PHP 7.2.5–8.6 and Guzzle ^7.15.
  • Note the 1.7 minor releases already carry forward-looking deprecations (baseUrl → baseUri, responseClass → responseModel, lowercase httpMethod, loose parameter schemas), so the 2.0 migration is pre-announced rather than a surprise — good governance, but it means the ^1.7 line is by definition transitional.

Technical risk

Risk Severity Notes
Guzzle major mismatch with Laravel High Pins us to ^1.7; blocks 2.0 adoption until Laravel moves to Guzzle 8
Hand-maintained descriptions drift from vendor API High No OpenAPI import; drift surfaces as runtime validation/parse errors, not build failures
Magic __call client surface Medium $client->getUser([...]) is untyped; PHPStan/IDE blind without @method annotations on a wrapper class
Response models are permissive Medium additionalProperties passes unknown JSON through; no compile-time contract enforcement
Description-array authoring learning curve Medium location, sentAs, parameter models, filters — non-obvious, and errors are caught at runtime
Transitive dependency surface Low Three Guzzle-org packages on the critical path; all actively maintained
PHP <8.7 ceiling on 2.0 Low A future constraint, not today's problem

Security posture is a positive: a published policy, Tidelift-mediated disclosure, a shipped fix for XML CDATA injection (GHSA-q8r6-5hfw-5jff), and 2.0 adds API-key redaction in PHP 8.2+ backtraces. For a package that will carry credentials into third-party APIs, that is the signal I want to see.

Key questions to resolve before commitment

  1. How many operations does the target API actually have? Below ~10, the Http facade plus a thin DTO layer is cheaper.
  2. Who owns the description arrays — us or the vendor? Is there a published machine-readable spec we could codegen from, even with a small in-house generator?
  3. Do we need async/concurrency (promises ^3, pools) for bulk fetch, or is sequential fine? This influences whether the 2.0 line is worth lobbying for.
  4. What is our Guzzle 8 timeline? If Laravel adoption is 12+ months out, we are committing to the 1.7 line for that whole window.
  5. Do we accept runtime-only contract validation, or do we budget for contract tests against the live sandbox in CI?

Integration Approach

Stack fit

  • Target Laravel 9+ on PHP 8.1–8.4, guzzlehttp/guzzle ^7.15. Composer: guzzlehttp/guzzle-services ^1.7. Do not allow ^2.0 in the constraint — a transitive resolution attempt would fail anyway, but pin explicitly and document why.
  • Coexistence is fine: Laravel's HTTP client keeps using the framework-bound Guzzle 7 client; guzzle-services builds its own GuzzleHttp\Client instance with its own HandlerStack. Keep those handlers separate so our retry/logging middleware does not leak onto outbound webhooks and mail-provider traffic.
  • Structure it as a first-party package (packages/acme/vendor-sdk or app/Services/{Vendor}) rather than loose code: one Description factory per external API, one ServiceProvider binding each client as a named singleton, config file for baseUri/credentials/timeouts.

Migration path

  • If starting from ad-hoc Guzzle or Http:: calls: extract the operation list first (endpoint, method, params, response shape) as a plain table, then transcribe to description arrays. Do not attempt a big-bang rewrite — new operations land on the service client, existing calls migrate opportunistically.
  • If starting from a Saloon-style or generated client: the description arrays are conceptually portable but not mechanically so; treat it as a rewrite of the transport layer only, keeping existing DTOs and mocks as the acceptance criteria.
  • Build a thin hand-written client subclass with @method annotations and typed wrapper returns. This is the single highest-leverage piece of glue: it restores IDE autocompletion and PHPStan coverage over the magic __call surface, and gives us one place to hang deprecation shims when we move to 2.0.
  • Map exceptions at the boundary: GuzzleHttp\Command\Exception\CommandException (validation), GuzzleHttp\Exception\RequestException/ConnectException (transport/HTTP), and JSON/XML parse failures into a small set of Laravel-native exceptions. Do not let Guzzle exceptions escape into controllers.

Compatibility

  • Watch the minimum Guzzle patch floor (^7.15) against whatever the platform pins. Laravel's own ^7.8-style constraint satisfies it, but an app with a hard pin at 7.4 or 7.10 will produce a resolution conflict and force a Guzzle bump — a small but real change-management item.
  • psr7 ^2.13 is required by 1.7.3+; older Laravel apps still on psr7 ^1 need an uplift.
  • Response-model behavior differs meaningfully across the 1.x → 2.0 boundary (body location returns string instead of a stream, scalar top-level JSON rejected, 512-element XML depth cap, strict enum matching). Any code written against 1.x assumptions should be flagged in the wrapper layer, not scattered across call sites.

Sequencing

  1. Spike (1 sprint): wire one API, 3–4 operations, behind the service-provider binding. Instrument with MockHandler in tests. Output: go/no-go on the description-array authoring cost.
  2. Wrapper layer: hand-written client subclass with @method annotations, exception mapping, and a HandlerStack carrying auth, correlation-ID logging, retry-with-backoff, and timeout policy.
  3. Coverage build-out: remaining operations in batches, each with a recorded-response test. Add a CI job that replays a small contract-test suite against the vendor sandbox weekly to catch drift.
  4. Laravel ergonomics: config file, php artisan vendor:publish, optional facade, health-check command.
  5. Observability: per-operation latency/error metrics keyed by operation name (the command layer gives you a natural label), plus 4xx/5xx breakdown.
  6. Deferred: evaluate the 2.0 line only when the Guzzle 8 upgrade lands in the framework; until then, suppress the deprecation notices and keep a written migration checklist against UPGRADING.md.

Operational Impact

Maintenance

  • Steady-state maintenance is low for the library itself and moderate for the descriptions. The package is released frequently (many patch releases across 1.7.x) but semver-disciplined: patch releases are PHP-version support, dependency floors, and edge-case serialization fixes; breaking changes are batched into majors. Predictable, which is what matters for planning.
  • The genuine ongoing cost is description ownership. Every vendor API change is a manual array edit plus a test fixture update. Budget a recurring, if small, allocation — this is the tax that an OpenAPI codegen pipeline would remove.
  • Upgrade exposure is real but bounded: the 1.7 minor line is explicitly transitional (deprecations for baseUrl, responseClass, method casing, loose schemas). Any new code should be written to the 2.0-compatible form now — that halves the eventual migration.

Support

  • Upstream support is credible: Guzzle org stewardship, a security policy with a private disclosure channel, Tidelift-backed commercial support available if we need an escalation path, and unusually thorough public docs (service descriptions, cookbook, extension points, upgrade guide).
  • Ecosystem support is thin. 254 stars and a reported dependent count of zero means near-zero StackOverflow/blog/consultant coverage for the specific failure modes we will hit. Internal knowledge concentration is a real risk: mitigate with a short ADR, a few worked examples in-repo, and mandatory test coverage on every description.
  • No Laravel-specific support layer — everything Laravel-shaped is ours to own.

Scaling

  • Descriptions are static config, so the client itself scales like Guzzle: stateless per-request, no shared mutable state beyond the handler stack. Bind as a singleton safely as long as credentials and baseUri are per-tenant resolved through a factory rather than reconfigured on a shared instance.
  • Validation and response-model hydration add per-call CPU overhead versus raw Guzzle — typically negligible, but it is work done on every request. If a single request fans out to dozens of operations, batch with promises (guzzlehttp/promises) rather than sequential calls.
  • Description parsing cost grows with operation count. For a several-hundred-operation surface, lazily instantiate the Description per service and avoid rebuilding it per request; consider caching the parsed structure if profiles show it.
  • Connection pooling, timeouts, and concurrency limits belong in the shared HandlerStack — set them once, centrally, or they will be inconsistent across operations.

Failure modes

Mode Symptom Mitigation
Description drift Validation errors or silently missing fields after a vendor change Weekly contract test against sandbox; schema-diff alerting
Parameter validation failure CommandException before any HTTP call Typed wrapper + unit tests per operation; surface as 4xx-class app error, never a 500
Transport failure ConnectException / timeout Handler-stack retry with backoff on idempotent methods only; circuit breaker on repeated failures
Malformed/unexpected payload JSON JsonException (2.0 uses native) or XML parse failure; 2.0 additionally rejects scalar top-level JSON and caps XML depth at 512 Fail closed, log the raw body with secrets redacted, alert on rate
Credential leakage Keys in logs or stack traces Rely on 2.0's #[\SensitiveParameter] redaction only where available; in 1.7 scrub at our logging middleware
Silent schema permissiveness Unknown JSON fields pass through unnoticed Contract tests asserting required fields; do not rely on additionalProperties for safety

Ramp-up

  • Expect 2–4 days for a mid-level Laravel developer to become productive: install, one worked example, exception mapping, and the test pattern. The description-array schema (location, sentAs, parameter models, filters) is the steep part and is learned by example, not by reading.
  • The magic __call client is the main discoverability tax. A hand-written subclass with @method annotations and typed returns is not optional polish — it is what makes the package tractable for a team that did not write the wiring, and it keeps PHPStan green in CI.
  • Onboarding asset: keep the working spike, one test per failure mode, and a one-page "add a new operation" checklist in-repo. Given the thin external ecosystem, that internal documentation is the difference between a maintainable integration and a bus-factor-one dependency.
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.
teamzac/geocoder
ideatica/config-writer
keaton-php/pipe-dream
jacksonwebservices/laravel-user-suite
maksimru/conditional-laravel-debugbar
gismatul/nova-support-chat
gismatul/laravel-support-chat
networkrailbusinesssystems/oracle-api
alphabalex/messaging-made-easy
rawphp/warp
shootkiran/merobug
narakode/fineauth
numencode/wn-syncops-plugin
ibnnajjaar/mib-global-pay
ably/pubsub-server
mstfkhazaal/defender
mfonte/base62x
karnoweb/metapilot
tobya/bcsapiwrapper
swalker2/cpanel