Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Laravel Graphql Voyager Laravel Package

mll-lab/laravel-graphql-voyager

Laravel GraphQL Voyager brings the interactive GraphQL Voyager interface to Laravel, making it easy to visualize and explore your GraphQL schema in the browser. With simple configuration and routes, it helps teams inspect types, relationships, and API structure during development.

View on GitHub
Deep Wiki
Context7

Getting Started

composer require mll-lab/laravel-graphql-voyager
php artisan vendor:publish --tag=graphql-voyager-config

The service provider is auto-discovered via Composer's extra.laravel.providers, so there is nothing to register in config/app.php or bootstrap/providers.php. After publishing, open config/graphql-voyager.php and confirm two values before you do anything else:

  • the endpoint the viewer should introspect (typically your Lighthouse /graphql route),
  • the route path the viewer is served from (usually /graphql-voyager).

Then php artisan serve and hit the viewer route. You should see Voyager's left-hand schema tree, the centre graph, and the right-hand details pane. If you get an empty graph, the viewer is booting but failing to reach or introspect the endpoint — see the Gotchas section.

First use case: onboarding a new developer onto a Lighthouse-backed API. Instead of running a query in GraphiQL and reading raw SDL, they open the Voyager route and click through Query → User → posts → Comment to see the schema shape. Keep this route enabled in local and any shared staging environment; disable it in production (see below).

Implementation Patterns

Gate the viewer, don't just obscure the URL. The published config exposes a middleware key (and, depending on version, an authorization callback). In non-local environments, wrap the route in a gate:

// config/graphql-voyager.php (typical shape — verify key names against the published file)
'middleware' => ['web', 'auth', 'can:viewGraphQLVoyager'],

Then define the ability in a policy or AuthServiceProvider:

Gate::define('viewGraphQLVoyager', fn ($user) => $user->isDeveloper());

This is the difference between "a nice internal tool" and "your entire API surface, including mutations, published to the internet."

Use a dedicated route only in the environments that need it. A common pattern is to guard registration in a small service provider rather than deleting the config entry:

// app/Providers/AppServiceProvider.php
public function register(): void
{
    if (! app()->environment(['local', 'staging'])) {
        config(['graphql-voyager.enabled' => false]);
    }
}

Check whether the package ships an enabled flag first — if it doesn't, conditionally re-register the provider or gate the middleware, since the route is registered by the package's provider regardless.

Point at remote or multiple schemas. Because the endpoint is a config value, you can serve one viewer route per schema by duplicating the config entry and registering an additional route in your own provider, or simply switch the endpoint per environment:

// config/graphql-voyager.php
'endpoint' => env('GRAPHQL_VOYAGER_ENDPOINT', '/graphql'),

This is useful in a monorepo where the public API and an internal admin API are separate Lighthouse schemas — stand up two viewer routes rather than trying to merge them.

Pair it with Lighthouse's dev tools. Voyager is a schema explorer, not a query runner. Keep GraphiQL (or Lighthouse's playground) alongside it: use Voyager to find the field you want, then paste the field path into the playground to actually run it. Documenting this two-tool workflow is the single highest-value thing you can put in your team's README.

Use it as living documentation in PRs. When a PR adds a type or changes a relationship, capture a Voyager screenshot in the description. It's a cheap, review-friendly artifact and it forces the author to check the schema actually renders — which catches broken type references that pass unit tests.

Gotchas and Tips

Introspection must be enabled on the target endpoint. Many production GraphQL setups disable introspection (correctly). If your viewer shows nothing, the first thing to check is whether the introspection query returns data — run it manually against the endpoint before you blame the package. This is also a reason not to expose the viewer in production: it needs introspection to be useful, and introspection in production is itself a disclosure.

Voyager runs in the browser. The viewer page loads Voyager's JS and issues the introspection request from the client, not from your PHP server. Consequences:

  • The GraphQL endpoint must be reachable from the browser, including through any CORS policy.
  • If your endpoint sits behind token auth, the viewer's request will 401 unless the auth is cookie/session-based (which is why pairing it with web middleware and session auth works, and token-only setups don't).
  • Behind a reverse proxy or in Docker, ensure the endpoint URL is the browser-visible one, not a container-internal hostname.

Config caching will bite you. Once you run php artisan config:cache in staging, edits to config/graphql-voyager.php do nothing until you php artisan config:clear. Standard Laravel behaviour, but it's a common "the middleware change didn't apply" report.

Asset publishing and CDN dependency. If the package bundles Voyager assets, publish them (vendor:publish --tag=graphql-voyager-assets, verify the tag) for air-gapped or strict-CSP environments. If it loads them from a CDN, your CSP must allow that origin — otherwise the page renders blank with a console-only error.

Route collisions. The default path (/graphql-voyager or similar) is fine, but if you already have a catch-all route or a prefix like /admin, move the viewer under your admin prefix via the config and middleware rather than fighting the router.

Laravel 11 specifics. v2.3.0 is the Laravel 11 compatibility release. Before you adopt it on L11, confirm (a) the composer.json constraint wasn't narrowed to ^11 only — if it was, this "minor" bump breaks Laravel 10 consumers on the next broad composer update; and (b) the package registers via extra.laravel.providers and merges config with mergeConfigFrom(), since Laravel 11's slim skeleton has no app/Http/Kernel.php to fall back on. Test with route:cache and config:cache enabled — provider-boot regressions in the slim skeleton tend to surface only when cached.

Treat exposure as the default failure mode. The single realistic production incident for this package is "we left the schema viewer reachable." Add the route to your smoke tests as an assertion that it 404s/403s in production, so a future config change can't silently re-expose it.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
cgv/localized-resource