15. Configuration reference
Publish the file with php artisan vendor:publish --tag=laragraph-config. Every key is optional; the defaults are shown. For a complete, working configuration, see example/config/laragraph.php.
default_schema
'default_schema' => 'default',The schema served at /graphql and used by Laragraph::execute() when you don't name one. It must be a key of schemas.
auth
'auth' => [
'default_guard' => null,
],| Key | Default | Meaning |
|---|---|---|
default_guard | null | The guard used by authorizeWithContext(), policy(), the per-user response cache and subscriber identities, when a field doesn't declare guards(). null means Laravel's default guard. See Authentication. |
discover
'discover' => [
'types' => 'app/GraphQL/Types',
'queries' => 'app/GraphQL/Queries',
'mutations' => 'app/GraphQL/Mutations',
'subscriptions' => 'app/GraphQL/Subscriptions',
],Directories (relative to the project root) that are scanned recursively for classes extending the matching base class. Set a key to '' to turn off discovery for that category. Discovered fields and types belong to every schema (see Multiple schemas). Cache the result in production with laragraph:cache.
route
'route' => [
'prefix' => 'graphql',
'middleware' => [],
'methods' => ['GET', 'POST'],
],| Key | Meaning |
|---|---|
prefix | URL prefix of every Laragraph route |
middleware | Route middleware for all Laragraph routes, including GraphiQL and unsubscribe (e.g. ['throttle:api']) |
methods | HTTP methods for schema endpoints that don't set their own method |
schemas
'schemas' => [
'default' => [
'query' => [], // 'fieldName' => QueryClass::class
'mutation' => [],
'subscription' => [],
'types' => [], // 'Alias' => TypeClass::class, only in this schema
'middleware' => [], // route middleware for this schema's endpoint
'method' => ['GET', 'POST'],
],
],See Multiple schemas.
types
'types' => [
'DateTime' => \Ayimdomnic\Laragraph\Scalars\DateTimeType::class,
'Date' => \Ayimdomnic\Laragraph\Scalars\DateType::class,
'JSON' => \Ayimdomnic\Laragraph\Scalars\JsonType::class,
'Upload' => \Ayimdomnic\Laragraph\Scalars\UploadType::class,
'UserRole' => \App\Enums\UserRole::class, // native PHP enums work too
],Types registered for every schema. Use it for scalars, enums and any type outside the discovery directories. See Types → Registering types.
error_formatter and errors_handler
'error_formatter' => [\Ayimdomnic\Laragraph\Laragraph::class, 'formatError'],
'errors_handler' => [\Ayimdomnic\Laragraph\Laragraph::class, 'handleErrors'],error_formatter(Error $error): array formats each error. errors_handler(array $errors, callable $formatter): array receives the whole list. Use array callables (not closures) so config:cache works. See Custom error formatting.
errors
'errors' => [
'negotiate_locale' => false,
'supported_locales' => ['en'],
'locale_resolver' => null,
],Controls per-request error localization; disabled by default (zero overhead — one config() call per request). See Error Handling & Localization.
security
'security' => [
'query_max_complexity' => 500,
'query_max_depth' => 15,
'disable_introspection' => null,
'max_aliases' => 30,
],| Key | Meaning |
|---|---|
query_max_complexity | Maximum total field cost per operation. null disables it. |
query_max_depth | Maximum selection nesting. The introspection query needs 11. null disables it. |
disable_introspection | null = disabled while app.debug is off; true/false forces it either way |
max_aliases | Maximum aliases per document. null disables it. |
See Security.
validation
'validation' => [
'rules' => [], // FQCNs of GraphQL\Validator\Rules\ValidationRule classes
],Extra document validation rules, resolved from the container and run on every operation. A rule of the same class as a built-in rule replaces it. Also available at runtime: Laragraph::addValidationRule().
pagination
'pagination' => [
'per_page' => 15,
'max_per_page' => 100,
],| Key | Meaning |
|---|---|
per_page | Page size when the client sends neither first nor last (or per_page) |
max_per_page | Upper bound for any requested page size. null = no cap. |
cache
'cache' => [
'response' => [
'enabled' => false,
'store' => 'default',
'ttl' => 60,
'scope' => 'user',
],
],| Key | Meaning |
|---|---|
enabled | Cache results of query operations |
store | Cache store name; 'default' = cache.default |
ttl | Seconds |
scope | 'user': one partition per user (on auth.default_guard) plus one shared guest partition. 'global': shared by everyone. |
persisted_queries
'persisted_queries' => [
'enabled' => false,
'store' => 'cache',
'ttl' => 3600,
'map' => [],
'apq' => true,
'only' => false,
],| Key | Meaning |
|---|---|
enabled | Accept queryId and APQ extensions.persistedQuery requests |
store | 'cache' (default cache store, supports runtime registration) or 'array' (the static map) |
ttl | Lifetime of cache-stored queries in seconds; null = forever |
map | id => query pairs for the array store. Key them by SHA-256 hash for trusted-documents mode. |
apq | Store a query when a client sends it together with its hash |
only | Trusted documents: execute only query text already stored under its hash |
See Persisted queries.
extensions
'extensions' => [
'request_id' => false,
'query_timing' => false,
'query_complexity' => false,
],Add extensions.requestId.id, extensions.timing.execution_ms, and extensions.queryComplexity.{cost,maxCost} (empty unless security.query_max_complexity is set) to every response. Register custom extensions with ExtensionRegistry::add(). See Observability.
middleware
'middleware' => [],Field middleware applied to every root field (queries, mutations, subscriptions), before the field's own middleware(). Entries are class names (resolved from the container) or instances of FieldMiddlewareInterface. See Field middleware.
logging
'logging' => [
'channel' => null,
],The log channel used by LoggingMiddleware and by the log subscription driver. null means the default channel.
log_unresolved_fields
'log_unresolved_fields' => null,null (default): warn when a field with no resolve{Field}Field() method resolves to null because nothing matched — no model attribute, no array key — following app.debug (loud in development, silent in production). true/false overrides that either way. Logged to logging.channel, not thrown — a resolver mistake shouldn't turn into a 500. See Types → Common mistakes.
database_types
'database_types' => [
'preset' => null, // 'postgres', 'cockroachdb', 'mssql', 'oracle'
'custom' => [], // 'Name' => ScalarClass::class
],Register scalars for native column types:
| Preset | Scalars |
|---|---|
postgres | UUID, BigInt, JSONB, Money, TSVector, Interval, Inet |
cockroachdb | UUID, BigInt, JSONB, Inet |
mssql | UUID, BigInt, Money |
oracle | UUID, BigInt, Interval |
See Types → Database scalar presets.
batching
'batching' => [
'enabled' => false,
'max_operations' => 10,
],Accept a JSON list of operations per request. max_operations of 0 removes the limit. See Batching.
graphiql
'graphiql' => [
'enabled' => null,
'middleware' => [],
'title' => 'Laragraph — GraphiQL',
],| Key | Meaning |
|---|---|
enabled | null = served only while app.debug is on; true = always; false = never |
middleware | Route middleware for the GraphiQL page, e.g. ['web', 'auth', 'can:viewGraphiql'] |
title | Page title |
tracing
'tracing' => [
'enabled' => false,
'driver' => 'apollo', // or 'otel'
'otel' => ['tracer_name' => 'laragraph'],
],Resolver timings, 'apollo' (default) under extensions.tracing, 'otel' exported as real OpenTelemetry spans instead. Development only. See Tracing.
octane
'octane' => [
'warm' => true,
],Under Laravel Octane, add Laragraph to octane.warm so each worker compiles the schema and validates each document once, instead of on every request. false leaves Octane's warm list alone. See Performance → Octane.
subscriptions
'subscriptions' => [
'enabled' => false,
'driver' => 'broadcast',
'cache_store' => null,
'ttl' => 3600,
'channel_prefix' => 'graphql-subscriber',
'authorize_channel' => true,
'queue' => [
'connection' => null,
'queue' => null,
],
'sse' => [
'max_duration' => 30,
'poll_interval_ms' => 500,
'heartbeat_seconds' => 15,
],
],| Key | Meaning |
|---|---|
enabled | Accept subscription operations. When off, they're rejected with an error. |
driver | 'broadcast' (Laravel Broadcasting), 'log' (write updates to logging.channel), or 'sse' (plain HTTP Server-Sent Events, no broadcaster needed) |
cache_store | Where subscriber registrations are stored; must be shared between servers. null = default store. |
ttl | Lifetime of a registration in seconds |
channel_prefix | Private channel each subscriber listens on: {prefix}.{subscriberId} ('broadcast' driver) |
authorize_channel | Register the rule that lets only the subscriber join their channel. false = write your own in routes/channels.php. |
queue.connection / queue.queue | Where broadcastLater() queues its job. null = application defaults. |
sse.max_duration | Seconds before an open 'sse'-driver connection self-closes (the client's EventSource reconnects transparently) |
sse.poll_interval_ms | How often an open connection checks for a new pending message |
sse.heartbeat_seconds | How often an SSE comment is sent on an otherwise idle connection, so proxies don't time it out |
See Subscriptions — read the 'sse' driver's capacity note before relying on it for more than a handful of concurrent subscribers.