harmbandstra/swagger-ui-bundle
Laravel package that bundles Swagger UI so you can serve interactive OpenAPI/Swagger documentation from your application. It publishes the required assets and provides routes/config to display your API spec with minimal setup.
composer require the package, then publish its assets and config:
php artisan vendor:publish --provider="L5Swagger\L5SwaggerServiceProvider"
This gives you config/l5-swagger.php (paths, route, middleware, scan targets) and the Swagger UI assets under public/vendor/.config/l5-swagger.php. The keys that matter day-to-day are paths.annotations (what gets scanned), paths.docs (where api-docs.json is written, typically storage/api-docs), routes.api (default /api/documentation), and generate_always.app/OpenApi/OpenApi.php, then annotate a single controller action, generate, and open the UI:
php artisan l5-swagger:generate
# browse to /api/documentation -> "Try it out" against one real endpoint
config.json in the project root and refresh the docs page:
{ "docExpansion": "none", "filter": true, "persistAuthorization": true, "displayRequestDuration": true }
No code change, no rebuild — the UI picks it up on view. That is the fastest way to confirm the new behaviour and to start tuning the docs UX for your team.OpenApi.php for @OA\Info, @OA\Server, @OA\SecurityScheme, and reusable @OA\Schema/@OA\Parameter components; controllers only carry per-operation annotations. This keeps diffs small when the spec evolves.paths.docs at it suits teams that treat OpenAPI as a contract reviewed before implementation. Mixing both in one document is where most confusion starts.php artisan l5-swagger:generate in your deploy pipeline (and in CI on every PR) and ship the resulting api-docs.json. Leave generate_always off in production so a page view never triggers a full annotation scan.middleware in the config (auth/basic-auth or an IP allowlist) and disable the route entirely outside local/staging. A Swagger UI page is a complete map of your attack surface.config.json as the team-wide UI contract. Commit it to the repo so every developer and every environment gets identical behaviour. High-value keys for day-to-day use:
docExpansion: "none" for large APIs, "list" for small onesfilter: true — the single biggest quality-of-life win on 100+ endpoint specspersistAuthorization: true — stops re-pasting tokens on every refreshtryItOutEnabled: true + displayRequestDuration: true in staging for manual smoke teststagsSorter / operationsSorter for deterministic ordering in reviewsdeepLinking: true so you can paste a link to a specific operation in a ticket or PRphp artisan l5-swagger:generate --all, giving each its own route and its own config.json-driven presentation if needed.openapi/info, and contains expected paths. Documentation drift then fails CI instead of being discovered by a consumer.api-docs.json; combined with deepLinking links this makes the Swagger UI the shared reference between backend and frontend.config.json is UI configuration, not spec configuration. It only affects how Swagger UI renders and behaves (expansion, filtering, auth persistence, interceptors). It does not change the generated OpenAPI document, validation, or servers — don't go looking there for schema problems..gitignore/.dockerignore.jq . config.json) if you rely on it.php artisan config:cache, and route cache can all mask changes. After editing config.json, hard-refresh; after changing route/path config, re-run config:clear and route:clear.config.json. On older versions the equivalent was additional_config_url (or a published, overridden index.blade.php). If you have multiple projects, confirm versions before documenting the workflow in a shared runbook.@OA\Server and proxies. Behind a load balancer or in Docker, relative/derived server URLs produce wrong "Try it out" targets. Set the server explicitly per environment or use a relative URL so requests follow the current host.storage/api-docs must be writable by the deploy user when generating on the server; the classic symptom is a silently stale api-docs.json.config.json covers your needs, keep it there. Reach for a published/overridden index.blade.php or custom middleware only when you need behaviour the Swagger UI config options genuinely cannot express — that keeps you on the upgrade path.How can I help you explore Laravel packages today?