API observability & intelligence for Laravel — local-first, privacy-first.
Track latency, error rates, and behavioral trends of your APIs. Everything stays on your machine.
composer require apiforge/apiforgephpRequires PHP ≥ 8.2 and the
pdo_sqliteextension (php8.x-sqlite3package on Debian/Ubuntu).
The service provider is auto-discovered by Laravel. Register the middleware in your bootstrap/app.php (Laravel 11+) or app/Http/Kernel.php (Laravel 10):
// Laravel 11+ — bootstrap/app.php
->withMiddleware(function (Middleware $middleware) {
$middleware->append(\ApiForge\Laravel\ApiForgeMiddleware::class);
})// Laravel 10 — app/Http/Kernel.php
protected $middleware = [
// ...
\ApiForge\Laravel\ApiForgeMiddleware::class,
];That's it. The local dashboard is now live at http://localhost:8000/_apiforge (adjust the port to match your Laravel app).
In local mode, the dashboard is served automatically at /_apiforge on your Laravel app's port — no separate process needed.
http://localhost:8000/_apiforge
- Health Score (0–100) — global API health at a glance
- Latency percentiles — P50 / P90 / P99 per route
- Error rates — 4xx and 5xx breakdown
- Automatic insights — latency anomalies, dead endpoints, release regressions
- Time series chart — click any route to see its latency over time
Publish the config file (optional):
php artisan vendor:publish --tag=apiforge-configOr configure via environment variables:
| Variable | Default | Description |
|---|---|---|
APIFORGE_ENV |
APP_ENV |
Environment label (production, staging…) |
APIFORGE_RELEASE |
APP_VERSION |
Release/version tag for regression tracking |
APIFORGE_SERVICE |
APP_NAME |
Service name |
APIFORGE_SAMPLING |
1.0 |
Sample rate 0.0–1.0 |
APIFORGE_DASHBOARD |
true |
Enable/disable the local dashboard routes |
APIFORGE_DASHBOARD_PREFIX |
_apiforge |
URL prefix for the local dashboard |
APIFORGE_CLOUD_URL |
— | Cloud mode: SaaS API base URL |
APIFORGE_API_KEY |
— | Cloud mode: project API key (af_…) |
Send metrics to the APIForge SaaS platform instead of storing them locally:
APIFORGE_CLOUD_URL=https://api.apiforge.fr
APIFORGE_API_KEY=af_your_project_key// bootstrap/app.php — same middleware registration, no code change needed
->withMiddleware(function (Middleware $middleware) {
$middleware->append(\ApiForge\Laravel\ApiForgeMiddleware::class);
})In cloud mode, events are buffered to a local temp file and flushed to the SaaS ingest API every 60 seconds. The local dashboard is disabled automatically.
Set the release tag to enable before/after deployment comparison:
APIFORGE_RELEASE=v1.4.0Or dynamically in AppServiceProvider:
config(['apiforge.release' => config('app.version')]);When a new release is detected, APIForge compares P90 latency before and after and surfaces regressions automatically.
- Per-route latency — P50, P90, P99 per endpoint
- Error rate by route — 2xx / 3xx / 4xx / 5xx breakdown
- API Health Score — a single 0–100 score summarising your API's health
- Ghost route detection — requests that match no declared Laravel route
- Latency anomaly alerts — Z-score detection against a 7-day baseline
- Dead endpoint detection — routes with no traffic for 21+ days
- Release regression analysis — automatic P90 comparison per deploy
- Progressive drift detection — slow latency increases over weeks
- Untracked route detection — declared routes that never received traffic
- Inflight concurrency tracking — approximate
inflight_avgandinflight_maxper route
The middleware flushes automatically at the end of every request. No manual teardown is needed for standard PHP-FPM deployments.
For long-running processes (Laravel Octane, FrankenPHP, Swoole), call shutdown() on application termination if needed — the aggregator is otherwise request-scoped and carries no state between requests.
| Limitation | Detail |
|---|---|
| TTFB = total duration | PHP-FPM sends responses atomically — true Time to First Byte cannot be measured at the middleware layer. lat_ttfb_* values equal lat_*. |
| Approximate inflight count | PHP workers are isolated; the inflight counter is shared via a file lock, which is accurate to the order of magnitude but not microsecond-precise. |
- Request and response bodies are never read
- No PII captured — only route patterns, HTTP methods, status codes, and timing
- Route parameters are normalised (
/users/42→/users/:id, UUIDs →/:uuid) - SQLite database stays entirely on your server in local mode
- Sampling rate can be set below 1.0 to reduce storage footprint
- PHP ≥ 8.2
- Laravel ≥ 10.0
ext-pdo_sqlite— install withapt install php8.x-sqlite3if not present
MIT — see LICENSE.