jooservices/laravel-client

Laravel integration for JOOservices Client: named HTTP connections, transport logging, and ops/activities/events wiring.

Maintainers

Package info

github.com/jooservices/laravel-client

pkg:composer/jooservices/laravel-client

Transparency log

Statistics

Installs: 0

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 5

v1.0.0 2026-08-04 18:30 UTC

README

CI codecov OpenSSF Scorecard PHP Version License: MIT Packagist Version Latest Release

Laravel integration for JOOservices Client. It turns the framework-agnostic HTTP client into a configured, named-connection service with container bindings, a facade, MongoDB transport logging, and wiring into the JOOservices logging, activities, and events packages.

The core package stays framework-agnostic. Everything Laravel-specific — config resolution, cache bridging, event dispatch, Eloquent reads — lives here.

Requirements

  • PHP 8.5+
  • Laravel 12 or 13
  • jooservices/client ^2.3

This package Composer-requires the JOOservices Laravel ecosystem (laravel-logging, laravel-activities, laravel-events, laravel-config, laravel-repository) because it targets JOO apps already on that stack. MongoDB transport logging and all three integrations are enabled by default for every connection; set integrations.* to false or change transport_log.driver when a connection needs different behavior. mongodb/laravel-mongodb is a direct dependency for the Mongo read model; the ext-mongodb extension is required when Mongo transport logging or reads are enabled.

Install

composer require jooservices/laravel-client
php artisan vendor:publish --tag=laravel-client-config

Publish and run the transport-log migration only if you set transport_log.driver to mysql:

php artisan vendor:publish --tag=laravel-client-migrations
php artisan migrate

Prune old MySQL transport rows (retention from transport_log.retention_days, default 30):

php artisan model:prune --model=JOOservices\\LaravelClient\\Models\\ClientRequestLog

Usage

Resolve a named connection through the facade, the manager, or the factory contract:

use JOOservices\LaravelClient\Facades\JOOClient;
use JOOservices\LaravelClient\Contracts\ClientFactoryInterface;

$stripe = JOOClient::connection('stripe');
$paypal = JOOClient::connection('paypal');

// Or via DI, programming to the contract.
$client = app(ClientFactoryInterface::class)->make('stripe');

Each connection is built once per process and cached. JOOClient::client() (no argument) returns the connection named by laravel-client.default, which is also what the container returns for JOOservices\Client\Contracts\HttpClientInterface.

Two payment APIs side by side

$charge = JOOClient::connection('stripe')->postJson('/v1/payment_intents', [
    'amount' => 4200,
    'currency' => 'usd',
], [
    'joo_activity' => [
        'subject' => $order,
        'activity' => 'payment.charged',
        'description' => 'Charged order via Stripe',
        'actor' => $request->user(),
    ],
    'joo_aggregate_id' => (string) $order->id,
    'joo_aggregate_type' => $order::class,
    // Optional; generated automatically when omitted.
    'joo_operation_id' => (string) Str::uuid(),
]);

$capture = JOOClient::connection('paypal')->postJson("/v2/checkout/orders/{$orderId}/capture", []);

joo_activity, joo_aggregate_id, joo_aggregate_type, and joo_operation_id are consumed by this package and stripped before the request reaches the transport. Relationship fields are propagated to transport-log context, ops-log context, activity data, and event metadata.

Customising a connection

use JOOservices\Client\Client\ClientBuilder;

JOOClient::extend('stripe', function (ClientBuilder $builder, string $name): void {
    $builder->withRateLimit(/* ... */);
});

extend() runs just before build() and discards any cached instance for that connection. Prefer config for auth/idempotency/retry/circuit breaker; use extend() for capabilities not mapped in config (see API reference).

Integrations

Each flag is per connection and on by default. Set an individual flag to false to disable that integration for a connection. Ecosystem packages are required at install time.

Config key Package What it does Fires when
transport_log.driver jooservices/client Raw request/response log to MySQL or Mongo. Mutually exclusive backends. Query through ClientRequestLogRepositoryInterface, which resolves the matching backend reader. Every request
integrations.logging jooservices/laravel-logging Ops trail via the http log adapter (http.outbound.completed / http.outbound.failed) Every request
integrations.activities jooservices/laravel-activities Business timeline entry against a subject Only when the call is tagged with joo_activity
integrations.events jooservices/laravel-events Persists an OutboundHttpRecorded event-sourced event Every request

OutboundHttpCompleted and OutboundHttpFailed are plain Laravel events dispatched for any connection with at least one integration enabled, so applications can listen to them directly without turning on a store.

Runtime overrides

With runtime_config.enabled, each connection may be overlaid from the jooservices/laravel-config store at client.connection_{name} (two-segment group.key path), stored as one JSON/array value per connection. Keys listed in runtime_config.protected_keys (headers, auth, transport_log by default) are stripped from the overlay: credentials stay in file and env configuration only.

Transport-log readers

Resolve ClientRequestLogRepositoryInterface for the configured default backend. It provides correlation lookup, recent failures, and recent pagination. Mongo uses MongoClientRequestLog and the application's mongodb/laravel-mongodb connection configured at transport_log.mongo.reader_connection; it must target the same database and collection as transport_log.mongo.uri, database, and collection. Both MySQL and Mongo models are read-only because jooservices/client owns writes.

Relationship observability (Mongo)

laravel-logging, laravel-activities, and laravel-events are MongoDB packages. The full relationship path (shared correlation_id / operation_id / aggregate fields across transport + ops + activities + events) is Mongo-first. Package defaults use transport_log.driver=mongo.

Resolve OutboundObservabilityRepositoryInterface for cross-store reporting (independently paginated results). That report API reads Mongo transport logs only — not MySQL transport rows.

MySQL (transport_log.driver=mysql) remains supported for raw transport dumps only. For full relationship reporting, stay on Mongo; for plain HTTP without the JOO Mongo stack, use integrations off and/or jooservices/client alone.

Known limitations

  • queue() on a log adapter only selects a queue target. Writes are asynchronous only when a worker is processing that queue.
  • Request and response body logging stays opt-in via transport_log.log_bodies.
  • recentFailures() is intended for ops/debug reads; prefer pruning so the table stays bounded.
  • InstrumentedHttpClient::batch() does not carry relationship tags (use per-call methods when tagging matters).
  • ClientRequestLogRepositoryInterface resolves from the default transport_log.driver only; mixed backends need the concrete repository.
  • With transport_log.driver=mongo (default), JOO_CLIENT_MONGO_URI and JOO_CLIENT_MONGO_DATABASE are required or make() throws.
  • Long-lived workers (Octane/queues): call JOOClient::forget() after runtime overlay changes; settle async promises so activity frames release.
  • Observability failures are swallowed so HTTP still succeeds; they dispatch ObservabilityFailed and best-effort log laravel-client.observability_failed.
  • Cross-store report (OutboundObservabilityRepository) is Mongo-only; MySQL transport is not included.

Development

composer lint:all
composer test
composer check

License

MIT. See LICENSE.