graphql-laravel 10.0.0
This is the stable release of the 10.x series.
Version 10 focuses on safer production defaults, cleaner core architecture, and moving the optional Eloquent query-selection machinery into its own package.
Before upgrading, read the Upgrade Guide. For the complete RC-by-RC history and pull request references, see the Changelog.
Most Important Upgrade Notes
SelectFields moved to a separate package
SelectFields is no longer part of core.
If you use SelectFields, install the new package:
composer require rebing/graphql-laravel-select-fieldsFor most users, this is enough. The package keeps the original namespaces and reads the same field config keys such as model, alias, selectable, always, is_relation, and query.
Core removals related to this extraction:
Rebing\GraphQL\Support\SelectFieldsremoved from coreClosuretype-hint inresolve()no longer auto-injects a SelectFields factory unless the external package is installedField::selectFieldClass()removedField::instanciateSelectFields()removed- Generated query/mutation stubs no longer include SelectFields boilerplate
Security defaults are stricter
Version 10 changes several defaults to be safer for production deployments:
- Schemas now default to
POSTonly - Batching is disabled by default
- Batch size is limited by
batching.max_batch_size, default10 - Introspection is disabled by default
- Query depth defaults to
13 - Query complexity defaults to
500 - Authorization now runs before validation
authorize()must return exactlytrue
If you previously relied on open defaults, explicitly configure them during upgrade.
To re-enable introspection, for example in development:
GRAPHQL_DISABLE_INTROSPECTION=falseTo re-enable GET requests:
'method' => ['GET', 'POST'],If you enable GET, also enable ReadOnlyOperationMiddleware after AutomaticPersistedQueriesMiddleware so mutations and subscriptions are rejected on GET requests.
Privacy signatures changed
Privacy::validate() now receives the parent/root object and field arguments:
-public function validate(array $queryArgs, $queryContext = null): bool
+public function validate(mixed $root, array $fieldArgs, mixed $queryContext = null, ?ResolveInfo $resolveInfo = null): boolPrivacy closures receive the same shape:
-'privacy' => function (array $args, $ctx): bool {
+'privacy' => function (mixed $root, array $args, $ctx, ?ResolveInfo $info = null): bool {The old first argument represented root query arguments. The new $fieldArgs contains the field's own arguments.
Middleware signatures changed
Resolver middleware now declares native mixed parameter and return types. Custom middleware overriding handle() must match:
-public function handle($root, array $args, $context, ResolveInfo $info, Closure $next)
+public function handle(mixed $root, array $args, mixed $context, ResolveInfo $info, Closure $next): mixedauthorize() signature changed
The unused $getSelectFields parameter was removed:
-public function authorize($root, array $args, $ctx, ?ResolveInfo $resolveInfo = null, ?Closure $getSelectFields = null): bool
+public function authorize($root, array $args, $ctx, ?ResolveInfo $resolveInfo = null): boolHighlights
OpenTelemetry tracing support
Version 10 adds tracing infrastructure with an OpenTelemetry driver.
New tracing components include:
TracingDriverTracingManagerTracingExecutionMiddlewareTracingResolverMiddlewareOpenTelemetryTracingDriver
Tracing is disabled by default and can be enabled globally or per schema.
CSRF protection middleware
A new opt-in HTTP middleware is available:
Rebing\GraphQL\Support\Middleware\CsrfGuard::classUse this for GraphQL endpoints that rely on cookie/session authentication, including Laravel session auth or Sanctum cookie mode.
Read-only GET enforcement
A new opt-in execution middleware rejects mutations and subscriptions submitted through GET:
Rebing\GraphQL\Support\ExecutionMiddleware\ReadOnlyOperationMiddleware::classThis is especially relevant if you enable GET for CDN-cacheable persisted queries.
Extensible resolver parameter injection
External packages can now hook into resolver parameter injection through:
Rebing\GraphQL\Support\Contracts\ResolverParameterInjectorField::registerParameterInjector()Field::clearParameterInjectors()
This is what allows the external SelectFields package to restore SelectFields injection without keeping it in core.
Fixes And Behavior Improvements
- APQ middleware validates queries before persisting them in cache
- APQ cache lookup race condition fixed
- APQ config no longer calls
config()inside the config file OperationParamsnow copiesoriginalInputandreadOnly- Route middleware is no longer duplicated when no per-schema middleware is defined
AddAuthUserContextValueMiddlewarenow resolves the guard from schema/global route config- Cross-field validation rules in nested InputTypes now work correctly
privacyon nested/sub-type fields is now enforced through field resolversGraphQL::type()has a narrower PHPStan return typemake:graphql:executionMiddlewareis now registered correctly- Minimum
webonyx/graphql-phpversion is now^15.31.0