douglasdc3/kong
A Laravel package for integrating with Kong API Gateway, providing a fluent interface to manage Kong Admin API resources such as services, routes, consumers, and plugins.
Understand what you're adopting first. This package is a thin HTTP wrapper around Kong's Admin API (:8001 by default), with a Laravel service provider, config file, and facade. It is a low-activity, single-maintainer project (26 stars, no dependents) whose recent releases are community-driven compatibility bumps rather than ongoing maintenance — v1.5.0 adds Laravel 10 support via a one-line constraint PR from a first-time contributor. Treat it as a vendored dependency you may need to fork, not an actively developed library. The upside: the Laravel 10 bump means you likely no longer need to fork just to install on a modern stack.
Install and publish config:
composer require douglasdc3/kong:^1.5
php artisan vendor:publish --provider="DouglasDC3\Kong\KongServiceProvider"
Pin ^1.5 (or at least verify the resolved version). Laravel 10 support landed only in v1.5.0. If Composer resolves an older 1.4.x — which it can, if your lockfile predates the release or a transitive constraint nudges it down — you silently get the 2023-era constraints and a package that refuses Laravel 10. Run composer show douglasdc3/kong and confirm you're on 1.5.*.
Look here first, in this order — the README is thin, so the source is the documentation:
config/kong.php — connection host/port, timeouts, and any default headers.src/KongServiceProvider.php — how the client is bound into the container and which facade alias is registered.src/ — these map 1:1 to Admin API collections (/services, /routes, /consumers, /plugins, /upstreams, /targets, /certificates, /snis). Method names track the endpoints (all(), find(), create(), update(), delete()).Configure via env, not hardcoded values:
KONG_ADMIN_URL=http://127.0.0.1:8001
KONG_ADMIN_TIMEOUT=5
Never point the Admin API at a public interface. It is unauthenticated by default and grants full control of the gateway; bind it to localhost or a private network, or front it with Kong's admin_gui_auth/enterprise RBAC.
First use case — expose a Laravel app through Kong. The canonical day-one task is registering your upstream as a Kong Service, attaching a Route, and layering a Plugin:
// Roughly the shape the package exposes; confirm exact signatures in src/.
$service = Kong::services()->create([
'name' => 'billing-api',
'url' => 'http://billing.internal:8080',
'protocol' => 'http',
]);
Kong::routes()->create([
'service' => ['id' => $service['id']],
'name' => 'billing-api-v1',
'paths' => ['/billing'],
'strip_path' => true,
]);
Kong::plugins()->create([
'service' => ['id' => $service['id']],
'name' => 'key-auth',
]);
Then smoke-test it end to end: curl -i http://localhost:8000/billing/health should return 401 from key-auth (proving the route matched and the plugin ran), not a connection error (proving the upstream resolved).
Wrap the facade in your own gateway service. Do not let Kong:: calls leak into controllers. Create app/Gateway/KongGateway.php that injects the manager, owns the retry/timeout policy, and returns your domain objects. When the package dies — or when the next Laravel major arrives and v1.5.0's constraints no longer cover it — you swap one class instead of grepping the codebase.
Treat Kong config as infrastructure-as-code, not request-scoped. The dominant real-world pattern is an idempotent sync command run on deploy:
// app/Console/Commands/SyncKong.php
foreach (config('gateway.services') as $def) {
$existing = Kong::services()->all(['name' => $def['name']])['data'][0] ?? null;
$existing
? Kong::services()->update($existing['id'], $def)
: Kong::services()->create($def);
}
Key rules for a sync command:
name, never assume an ID. Kong generates UUIDs; names are your stable key.409 Conflict on duplicate unique names, which will fail every redeploy after the first.billing-*) so you never delete objects owned by another team.Split config per environment. Use config/gateway.php with an env-selected connection, and keep a .env per environment. Never let a staging deploy reach production's Admin API — a wrong KONG_ADMIN_URL in CI is the classic incident here. Guard it: assert app()->environment() matches the Kong host before mutating.
Consumer provisioning on signup. A common pattern is creating a Kong consumer and a key-auth credential when a tenant registers, then storing the key in your DB:
$consumer = Kong::consumers()->create(['username' => $tenant->slug]);
$cred = Kong::consumers()->createCredential($consumer['id'], 'key-auth');
// persist $cred['key'] in your tenants table, show it once like an API token
Never re-fetch and re-display keys on every page load — Kong returns credentials in plaintext, and you don't want them in HTML, logs, or Sentry.
CI/CD integration. Run php artisan kong:sync as a post-deploy step, after migrations and after the app is healthy enough to be a valid upstream. Fail the deploy loudly if sync fails — a half-registered gateway is worse than an unregistered one.
Testing. Do not hit a real Kong in tests. Two workable strategies:
Http::fake() the Admin API endpoints, and assert your gateway service issues the expected requests. This tests your code, which is what you actually own.KongGateway interface into the container for feature tests, and keep one small integration suite (marked @group integration) that runs against a Kong container in CI.Observability integration. Since you're already talking to the Admin API, use it for read-only dashboards: list upstream targets and health status, count consumers, surface plugin inventory. Read-only usage is the safest long-term way to depend on a low-activity package.
v1.5.0 is a constraints bump, not a maintenance signal. The release contains exactly one change — Laravel 10 support — with no API changes, no bug fixes, and no deprecations. That means:
repositories entry at your fork. Budget a day; don't discover it on release day.PUT vs PATCH semantics are traps. Kong's Admin API distinguishes replace (PUT/PATCH to a specific ID) from upsert at a URL (PUT /services/{name}). The package's update() likely issues a PATCH. That means nested fields merge rather than replace — removing a route path from your config array may silently leave the old path in Kong. Verify the behavior, and for hard cutovers delete-then-create (briefly disruptive) or explicitly null the fields.
Plugin config blocks are schemaless from PHP's perspective. Kong validates them server-side against the plugin's JSON schema; the package will happily ship garbage and Kong will reject it with a terse 400. Worse, the correct config keys differ across Kong versions and between OSS and Enterprise. Pin your Kong version in documentation, and keep a known-good config block per plugin in your test fixtures.
Cascading deletes. Deleting a service deletes its routes and plugins. Deleting an upstream deletes its targets. In a sync command this means a typo in your config file can wipe a route a customer depends on. Log every destructive operation with the object name before issuing it, and gate deletes behind an explicit --prune flag.
DB-less / declarative Kongs are read-only. If your infrastructure runs Kong in DB-less mode (Kong Ingress Controller, many Kubernetes setups), the Admin API is a status endpoint only. This package, and any Admin-API client, is useless there — use deck and declarative YAML instead. Confirm your Kong deployment mode before writing any of this code.
Timeouts will hang your deploys. A default Guzzle timeout of 0 means infinite. Set an explicit connect/read timeout (3–10s) in the config, and add one bounded retry for connection errors only — not for 4xx, which are deterministic. v1.5.0 did not change any of this.
Pagination is manual. Kong paginates with size and an opaque offset cursor (not page numbers). Any all() that silently returns only the first hundred objects will break your sync command once you exceed 100 consumers or routes. Loop on offset until it's absent.
Credentials and secrets come back in cleartext. key-auth keys, basic-auth passwords, JWT secrets, and any config value containing an API key are all readable by anyone who can reach :8001. This is the single biggest reason to lock down the Admin port, and a reason to be paranoid about what you log — the package's exception messages may include response bodies containing secrets.
Debugging checklist. When a call fails: log the raw HTTP status and body (Kong's error payloads are informative), confirm the Admin URL from php artisan tinker (config('kong.host')), check whether php artisan config:cache froze a stale env value, and verify the object exists via curl $KONG_ADMIN_URL/services before assuming a package bug. Also confirm the resolved package version — a stale lockfile on 1.4.x behaves differently on Laravel 10 and is an easy dead end.
Extension points are minimal, so go around it. There's little in the way of middleware, events, or a pluggable HTTP client. For anything the package doesn't model (new Kong entities, workspaces in Enterprise, /status, /config), drop to a plain Http::withHeaders(...)->get("$adminUrl/...") call in your own gateway class. Mixing the two is fine — just keep it behind one interface.
Know when to walk away. For a new project with declarative configuration and a GitOps flow, deck is the maintained, supported answer and this package is not. Choose this package only if you genuinely need imperative, runtime gateway mutation from PHP — tenant provisioning, per-request consumer creation, dynamic plugin toggling — and you accept ownership of the fork when Laravel 11+ arrives.
How can I help you explore Laravel packages today?