External service integrations and API logging for Laravel Enso

Maintainers

Package info

github.com/laravel-enso/api

pkg:composer/laravel-enso/api

Statistics

Installs: 23 169

Dependents: 4

Suggesters: 0

Stars: 0

Open Issues: 1


README

License Stable Downloads PHP Issues Merge Requests

Description

API is Laravel Enso's reusable package for external service integrations and inbound API request logging.

It provides a small action-based client on top of Laravel's HTTP client, optional SOAP transport through PHP's SoapClient, reusable contracts for authentication, retries, query parameters, file uploads, form payloads, and timeouts. On top of that, it standardizes inbound and outbound API logging, admin notifications for failed calls, and small payload-building helpers used across Enso integrations.

Installation

This package comes pre-installed in Laravel Enso distributions that integrate external services or expose protected internal API endpoints.

For standalone package installation inside an Enso-based application:

composer require laravel-enso/api

The package auto-registers its service provider, loads its migrations, and registers:

  • the api-action-logger middleware alias
  • the core-api middleware group
  • the enso.api configuration namespace

Run the migrations after installation:

php artisan migrate

If you need to inspect raw request and response traffic during integration work, enable the debug flag in your environment:

API_DEBUG=true

Features

  • Wraps outbound integrations in Action classes that resolve and execute Endpoint definitions.
  • Builds requests through Laravel's HTTP client with support for:
    • bearer-token authentication
    • basic authentication
    • custom headers
    • query parameters
    • form submissions
    • file attachments
    • custom timeouts
    • retry policies
  • Supports SOAP integrations through SoapEndpoint, SoapApi, SoapResponse, and the optional Endpoints\Soap base class.
  • Refreshes an expiring auth token once automatically when an authenticated endpoint receives 401 or 403 on the first try.
  • Logs outbound calls in api_logs, including URL, route, HTTP method, status, attempt number, payload, direction, and duration.
  • Logs inbound calls through the ApiLogger middleware and reports non-200 responses to administrators.
  • Provides a read-only System > API Logs table, with filters for user, permission, method, direction, and creation datetime.
  • Queues ApiCallError notifications to active Enso admins on the notifications queue using shared laravel-enso/mails layouts and package-owned previews.
  • Provides Resource and Filter base classes for payload shaping and input validation.
  • Includes a Throttle helper for debouncing repeated external API calls.
  • Ships the api_logs migration and an Api\Models\Log model with Enso caching traits.

::: tip Tip If an endpoint implements UsesAuth and the first response is 401 or 403, Api::call() refreshes the token once through the token provider before continuing with the normal retry flow. :::

Usage

Outbound action

Define a small endpoint class that describes the request:

use LaravelEnso\Api\Contracts\Endpoint;
use LaravelEnso\Api\Enums\Method;

class FetchOffers implements Endpoint
{
    private array $filters = [];

    public function method(): Method
    {
        return Method::GET;
    }

    public function url(): string
    {
        return 'https://posf.ro/api/v1/comparator';
    }

    public function filters(array $filters): self
    {
        $this->filters = $filters;

        return $this;
    }

    public function body(): array
    {
        return $this->filters;
    }
}

Wrap it in an Action and call handle():

use LaravelEnso\Api\Action;

class FetchOffersAction extends Action
{
    public function __construct(private array $filters)
    {
    }

    protected function endpoint(): Endpoint
    {
        return (new FetchOffers())->filters($this->filters);
    }
}

$response = (new FetchOffersAction([
    'request' => 'comparator-electric',
    'tip_client' => 'casnic',
]))->handle();

$payload = $response->json();

SOAP action

SOAP integrations use the same Action orchestration and logging flow. Extend the optional SOAP endpoint base class when the operation can be represented by wsdl(), operation(), and arguments():

use LaravelEnso\Api\Endpoints\Soap;

class SubmitInvoice extends Soap
{
    public function __construct(private Invoice $invoice)
    {
    }

    public function wsdl(): ?string
    {
        return config('services.invoices.wsdl');
    }

    public function operation(): string
    {
        return 'SubmitInvoice';
    }

    public function arguments(): array
    {
        return [[
            'number' => $this->invoice->number,
            'total' => $this->invoice->total,
        ]];
    }

    public function options(): array
    {
        return [
            'trace' => true,
            'exceptions' => true,
            'connection_timeout' => 30,
        ];
    }
}

Then return the SOAP endpoint from an action:

use LaravelEnso\Api\Action;
use LaravelEnso\Api\Contracts\Endpoint;

class SubmitInvoiceAction extends Action
{
    public function __construct(private Invoice $invoice)
    {
    }

    protected function endpoint(): Endpoint
    {
        return new SubmitInvoice($this->invoice);
    }
}

$response = (new SubmitInvoiceAction($invoice))->handle();

$result = $response->body();

SOAP responses are wrapped in SoapResponse. Failed SOAP calls return a failed response internally, are logged, trigger the normal notification flow, and then throw the original SoapFault from handle().

Inbound logging

Use the standalone middleware alias when you only want request logging:

Route::middleware(['auth', 'api-action-logger'])
    ->prefix('internal')
    ->group(function (): void {
        Route::post('sync', SyncController::class)->name('internal.sync');
    });

Or use Enso's core-api middleware group when the route also needs active-state checks, permission checks, and localisation handling:

Route::middleware(['auth', 'core-api'])
    ->prefix('api')
    ->group(function (): void {
        Route::post('calendar/events', EventController::class)->name('calendar.events.store');
    });

Resource and filter helpers

Use Resource when a payload needs nested resource resolution and mandatory attribute validation:

use LaravelEnso\Api\Resource;

class OfferResource extends Resource
{
    public function __construct(private array $offer)
    {
    }

    public function toArray(): array
    {
        return [
            'id' => $this->offer['id'],
            'supplier' => $this->offer['supplier'],
        ];
    }

    protected function mandatoryAttributes(): array
    {
        return ['id', 'supplier'];
    }
}

Use Filter when you want to reject unsupported keys before sending them to the remote service:

use LaravelEnso\Api\Filter;

class OfferFilters extends Filter
{
    public function allowed(): array
    {
        return ['request', 'tip_client', 'consum_lunar'];
    }
}

$filters = (new OfferFilters($input))->toArray();

API

Core classes

  • LaravelEnso\Api\Api Builds and executes the HTTP request, applies optional contracts, tracks attempt count, refreshes bearer tokens once when needed, and retries failed calls when the endpoint allows it.
  • LaravelEnso\Api\SoapApi Builds and executes SOAP calls through PHP's SoapClient, applies SOAP headers when provided, tracks attempt count, and retries SoapFault failures when the endpoint allows it.
  • LaravelEnso\Api\SoapResponse Wraps successful SOAP results and failed SoapFault instances behind the response methods used by Action.
  • LaravelEnso\Api\Action Orchestrates one outbound call, measures duration, persists the outbound log entry, reports failures, and returns either an Illuminate\Http\Client\Response for HTTP endpoints or a SoapResponse for SOAP endpoints.
  • LaravelEnso\Api\Endpoints\Soap Optional base class for SOAP endpoints. It maps SOAP calls onto the existing Endpoint contract by defaulting method() to Method::POST, url() to the WSDL, location, or operation, and body() to the SOAP arguments.

Contracts

Required contract:

  • Endpoint Defines method(), url(), and body().

Optional contracts:

  • Client Internal transport contract implemented by Api and SoapApi.
  • UsesAuth Adds bearer token support through a token provider.
  • UsesBasicAuth Adds HTTP basic authentication.
  • CustomHeaders Adds arbitrary request headers.
  • QueryParameters Appends parameters() to the request URL.
  • Retry Enables retries through tries() and delay().
  • Timeout Sets a custom HTTP timeout.
  • AsForm Sends the payload as form data.
  • AttachesFiles Attaches files to the pending request.
  • SoapEndpoint Extends Endpoint and defines wsdl(), operation(), arguments(), and options() for SOAP calls.
  • SoapHeaders Adds SOAP headers through SoapClient::__setSoapHeaders().
  • Token Defines the token provider contract used by UsesAuth.

::: warning Note SOAP support is additive. Existing HTTP endpoints keep using the same Endpoint contract and Api client behavior. Applications only need the PHP SOAP extension when they actually use SoapEndpoint. :::

Middleware

  • api-action-logger Alias for LaravelEnso\Api\Http\Middleware\ApiLogger
  • core-api Middleware group registered by the service provider:
    • LaravelEnso\Core\Http\Middleware\VerifyActiveState
    • LaravelEnso\Api\Http\Middleware\ApiLogger
    • LaravelEnso\Permissions\Http\Middleware\VerifyRouteAccess
    • LaravelEnso\Localisation\Http\Middleware\SetLanguage

Log model and storage

LaravelEnso\Api\Models\Log

Stored attributes:

  • user_id
  • route
  • url
  • payload
  • method
  • status
  • try
  • direction
  • duration
  • created_at
  • updated_at

Relationships and casts:

  • user() Belongs to LaravelEnso\Users\Models\User
  • payload Cast to array
  • method Cast to LaravelEnso\Api\Enums\Method
  • direction Cast to LaravelEnso\Api\Enums\Direction

Logging directions:

  • Direction::Inbound
  • Direction::Outbound

API Logs table

The package provides the System > API Logs table through:

  • GET /api/system/apiLogs/initTable
  • GET /api/system/apiLogs/tableData
  • GET /api/system/apiLogs/exportExcel

Permissions:

  • system.apiLogs.index
  • system.apiLogs.initTable
  • system.apiLogs.tableData
  • system.apiLogs.exportExcel

The table defaults to api_logs.created_at desc, exposes created_at as a datetime column, displays resolved users and permissions, and supports filters for user, permission, method, direction, and creation datetime.

Upgrade notes

This release changes the public logging enum contract:

  • replace LaravelEnso\Api\Enums\Calls with LaravelEnso\Api\Enums\Direction
  • replace Calls::Inbound / Calls::Outbound with Direction::Inbound / Direction::Outbound
  • replace LaravelEnso\Api\Enums\Methods with LaravelEnso\Api\Enums\Method
  • replace Method::get, Method::post, Method::put, and Method::delete with Method::GET, Method::POST, Method::PUT, and Method::DELETE
  • update endpoint signatures from method(): string to method(): Method
  • update application code or reporting queries that reference api_logs.type to use api_logs.direction
  • update frontend API log filters from apiLogMethods to apiLogMethod

After updating the package, run:

composer update laravel-enso/api
php artisan enso:upgrade

Resource, filter, and throttle helpers

  • Resource::resolve() Resolves nested resources and validates mandatory attributes.
  • Resource::collection() Maps a collection into resolved resource arrays.
  • Resource::toJson() Serializes the resource payload.
  • Filter::toArray() Rejects unsupported keys before returning the filter array.
  • Throttle::__invoke() Debounces repeated calls by sleeping until the configured interval has elapsed.

::: warning Note Inbound logging reports any non-200 response through ApiCallError, while outbound actions report failed responses and thrown exceptions through the same notification pipeline.

The notification handler targets active Enso admins and queues the email on the notifications queue. :::

Depends On

Required Enso packages:

Framework dependency:

Contributions

are welcome. Pull requests are great, but issues are good too.

Thank you to all the people who already contributed to Enso!