PHP code example of rebing / graphql-laravel

1. Go to this page and download the library: Download rebing/graphql-laravel library. Choose the download type require.

2. Extract the ZIP file and open the index.php.

3. Add this code to the index.php.
    
        
<?php
require_once('vendor/autoload.php');

/* Start to develop here. Best regards https://php-download.com/ */

    

rebing / graphql-laravel example snippets


declare(strict_types = 1);
namespace App\GraphQL\Types;

use GraphQL\Type\Definition\Type;
use Rebing\GraphQL\Support\Type as GraphQLType;

class BookType extends GraphQLType
{
    protected $attributes = [
        'name' => 'Book',
        'description' => 'A book',
    ];

    public function fields(): array
    {
        return [
            'id' => [
                'type' => Type::nonNull(Type::int()),
                'description' => 'The id of the book',
            ],
            'title' => [
                'type' => Type::nonNull(Type::string()),
                'description' => 'The title of the book',
            ],
            'author' => [
                'type' => Type::string(),
                'description' => 'The name of the author',
            ],
        ];
    }
}

declare(strict_types = 1);
namespace App\GraphQL\Queries;

use GraphQL\Type\Definition\Type;
use Rebing\GraphQL\Support\Facades\GraphQL;
use Rebing\GraphQL\Support\Query;

class BooksQuery extends Query
{
    protected $attributes = [
        'name' => 'books',
    ];

    public function type(): Type
    {
        return Type::nonNull(Type::listOf(Type::nonNull(GraphQL::type('Book'))));
    }

    public function args(): array
    {
        return [
            'title' => [
                'type' => Type::string(),
                'description' => 'Filter by title',
            ],
        ];
    }

    public function resolve($root, array $args): array
    {
        $books = [
            ['id' => 1, 'title' => 'The Great Gatsby', 'author' => 'F. Scott Fitzgerald'],
            ['id' => 2, 'title' => '1984', 'author' => 'George Orwell'],
            ['id' => 3, 'title' => 'To Kill a Mockingbird', 'author' => 'Harper Lee'],
        ];

        if (isset($args['title'])) {
            return array_values(array_filter($books, fn ($book) => str_contains($book['title'], $args['title'])));
        }

        return $books;
    }
}

'schemas' => [
    'default' => [
        'query' => [
            App\GraphQL\Queries\BooksQuery::class,
        ],
        'mutation' => [],
        'types' => [
            App\GraphQL\Types\BookType::class,
        ],
    ],
],

'default_schema' => 'default',

'schemas' => [
    'default' => [
        'query' => [
            ExampleQuery::class,
        ],
        'mutation' => [
            ExampleMutation::class,
        ],
        'types' => [
        
        ],
    ],
    'user' => [
        'query' => [
            App\GraphQL\Queries\ProfileQuery::class
        ],
        'mutation' => [

        ],
        'types' => [
        
        ],
        'middleware' => ['auth:api'],
        // Which HTTP methods to support; must be given in UPPERCASE!
        // Default is POST only; enable GET explicitly if needed
        'method' => ['GET', 'POST'], 
        'execution_middleware' => [
            \Rebing\GraphQL\Support\ExecutionMiddleware\UnusedVariablesMiddleware::class,
        ],
        // Route attributes applied to the generated HTTP route for this schema
        // Example: expose this schema on a dedicated subdomain
        'route_attributes' => [
            'domain' => 'api.example.com',
        ],
        // Per-schema route group attributes. The `guard` entry is consumed by
        // the built-in `AddAuthUserContextValueMiddleware` to populate the
        // GraphQL context value (`$ctx`) from the named guard. Defaults to
        // the application's default guard when unset.
        'group_attributes' => [
            'guard' => 'api',
        ],
        // Override the default controller for this schema.
        // Supports string ('Class@method') and array ([Class::class, 'method']) formats.
        // The controller method receives the same parameters as GraphQLController@query.
        // 'controller' => App\Http\Controllers\MyGraphQLController::class . '@query',
    ],
],

'schemas' => [
    'with_custom_domain' => [
        'query' => [
            App\GraphQL\Queries\UsersQuery::class,
        ],
        'middleware' => ['auth:api'],
        'route_attributes' => [
            'domain' => 'api.example.com',
        ],
    ],
]

'schemas' => [
    'default' => DefaultSchema::class
]

declare(strict_types = 1);
namespace App\GraphQL\Schemas;

use Rebing\GraphQL\Support\Contracts\ConfigConvertible;

class DefaultSchema implements ConfigConvertible
{
    public function toConfig(): array
    {
        return [
            'query' => [
                ExampleQuery::class,
            ],
            'mutation' => [
                ExampleMutation::class,
            ],
            'types' => [
            
            ],
        ];
    }
}

declare(strict_types = 1);
namespace App\GraphQL\Types;

use App\Models\User;
use GraphQL\Type\Definition\Type;
use Rebing\GraphQL\Support\Facades\GraphQL;
use Rebing\GraphQL\Support\Type as GraphQLType;

class UserType extends GraphQLType
{
    protected $attributes = [
        'name'          => 'User',
        'description'   => 'A user',
        // Note: only necessary if you use the SelectFields package
        'model'         => User::class,
    ];

    public function fields(): array
    {
        return [
            'id' => [
                'type' => Type::nonNull(Type::string()),
                'description' => 'The id of the user',
                // Use 'alias', if the database column is different from the type name.
                // This is supported for discrete values as well as relations.
                // - you can also use `DB::raw()` to solve more complex issues
                // - or a callback returning the value (string or `DB::raw()` result)
                'alias' => 'user_id',
            ],
            'email' => [
                'type' => Type::string(),
                'description' => 'The email of user',
                'resolve' => function($root, array $args) {
                    // If you want to resolve the field yourself,
                    // it can be done here
                    return strtolower($root->email);
                }
            ],
            // Uses the 'getIsMeAttribute' function on our custom User model
            'isMe' => [
                'type' => Type::boolean(),
                'description' => 'True, if the queried user is the current user',
                'selectable' => false, // Does not try to query this from the database
            ],
            // Reference another registered GraphQL type for relations
            'profile' => [
                'type' => GraphQL::type('UserProfile'),
                'description' => 'The user profile',
            ],
        ];
    }

    // You can also resolve a field by declaring a method in the class
    // with the following format resolve[FIELD_NAME]Field()
    protected function resolveEmailField($root, array $args)
    {
        return strtolower($root->email);
    }
}

'schemas' => [
    'default' => [
        // ...
        
        'types' => [
            App\GraphQL\Types\UserType::class,
        ],

  'types' => [
      App\GraphQL\Types\UserType::class,
  ],
  

  GraphQL::addType(\App\GraphQL\Types\UserType::class);
  

  GraphQL::addTypes([
      \App\GraphQL\Types\UserType::class,
      'CustomName' => \App\GraphQL\Types\PostType::class,
  ]);
  

declare(strict_types = 1);
namespace App\GraphQL\Queries;

use Closure;
use App\Models\User;
use Rebing\GraphQL\Support\Facades\GraphQL;
use GraphQL\Type\Definition\ResolveInfo;
use GraphQL\Type\Definition\Type;
use Rebing\GraphQL\Support\Query;

class UsersQuery extends Query
{
    protected $attributes = [
        'name' => 'users',
    ];

    public function type(): Type
    {
        return Type::nonNull(Type::listOf(Type::nonNull(GraphQL::type('User'))));
    }

    public function args(): array
    {
        return [
            'id' => [
                'type' => Type::string(),
            ],
            'email' => [
                'type' => Type::string(),
            ]
        ];
    }

    public function resolve($root, array $args, $context, ResolveInfo $resolveInfo)
    {
        if (isset($args['id'])) {
            return User::where('id' , $args['id'])->get();
        }

        if (isset($args['email'])) {
            return User::where('email', $args['email'])->get();
        }

        return User::all();
    }
}

'schemas' => [
    'default' => [
        'query' => [
            App\GraphQL\Queries\UsersQuery::class
        ],
        // ...
    ]
]

declare(strict_types = 1);
namespace App\GraphQL\Queries;

use App\Models\User;
use GraphQL\Type\Definition\ResolveInfo;
use GraphQL\Type\Definition\Type;
use Rebing\GraphQL\Support\Facades\GraphQL;
use Rebing\GraphQL\Support\Query;

class UserQuery extends Query
{
    protected $attributes = [
        'name' => 'user',
    ];

    public function type(): Type
    {
        return Type::nonNull(GraphQL::type('User'));
    }

    public function args(): array
    {
        return [
            'id' => [
                'type' => Type::nonNull(Type::int()),
            ],
        ];
    }

    public function resolve($root, array $args, $context, ResolveInfo $resolveInfo)
    {
        return User::findOrFail($args['id']);
    }
}

declare(strict_types = 1);
namespace App\GraphQL\Mutations;

use Closure;
use App\Models\User;
use Illuminate\Support\Facades\Hash;
use Rebing\GraphQL\Support\Facades\GraphQL;
use GraphQL\Type\Definition\Type;
use GraphQL\Type\Definition\ResolveInfo;
use Rebing\GraphQL\Support\Mutation;

class UpdateUserPasswordMutation extends Mutation
{
    protected $attributes = [
        'name' => 'updateUserPassword'
    ];

    public function type(): Type
    {
        return Type::nonNull(GraphQL::type('User'));
    }

    public function args(): array
    {
        return [
            'id' => [
                'type' => Type::nonNull(Type::string()),
            ],
            'password' => [
                'type' => Type::nonNull(Type::string()),
            ]
        ];
    }

    public function resolve($root, array $args, $context, ResolveInfo $resolveInfo)
    {
        $user = User::find($args['id']);
        if(!$user) {
            return null;
        }

        $user->password = Hash::make($args['password']);
        $user->save();

        return $user;
    }
}

'schemas' => [
    'default' => [
        'mutation' => [
            App\GraphQL\Mutations\UpdateUserPasswordMutation::class,
        ],
        // ...
    ]
]

declare(strict_types = 1);
namespace App\GraphQL\Mutations;

use GraphQL\Type\Definition\Type;
use Rebing\GraphQL\Support\Facades\GraphQL;
use Rebing\GraphQL\Support\Mutation;

class LoginMutation extends Mutation
{
    protected $attributes = [
        'name' => 'login',
        'description' => 'Log in by email and password',
    ];

    public function type(): Type
    {
        return GraphQL::type('User');
    }

    public function args(): array
    {
        return [
            'email' => [
                'type' => Type::nonNull(Type::string()),
                'rules' => ['

'types' => [
    \Rebing\GraphQL\Support\UploadType::class,
],

declare(strict_types = 1);
namespace App\GraphQL\Mutations;

use Closure;
use GraphQL\Type\Definition\ResolveInfo;
use GraphQL\Type\Definition\Type;
use Rebing\GraphQL\Support\Facades\GraphQL;
use Rebing\GraphQL\Support\Mutation;

class UserProfilePhotoMutation extends Mutation
{
    protected $attributes = [
        'name' => 'userProfilePhoto',
    ];

    public function type(): Type
    {
        return GraphQL::type('User');
    }

    public function args(): array
    {
        return [
            'profilePicture' => [
                'type' => GraphQL::type('Upload'),
                'rules' => ['

class UpdateUserEmailMutation extends Mutation
{
    //...

    public function args(): array
    {
        return [
            'id' => [
                'type' => Type::string(),
                'rules' => ['

declare(strict_types = 1);
namespace App\GraphQL\Mutations;

use Closure;
use App\Models\User;
use Rebing\GraphQL\Support\Facades\GraphQL;
use GraphQL\Type\Definition\ResolveInfo;
use GraphQL\Type\Definition\Type;
use Rebing\GraphQL\Support\Mutation;

class UpdateUserEmailMutation extends Mutation
{
    protected $attributes = [
        'name' => 'updateUserEmail'
    ];

    public function type(): Type
    {
        return GraphQL::type('User');
    }

    public function args(): array
    {
        return [
            'id' => [
                'type' => Type::string(),
            ],
            'email' => [
                'type' => Type::string(),
            ]
        ];
    }

    protected function rules(array $args = []): array
    {
        return [
            'id' => ['

protected function resolve($root, array $args) {
    \Illuminate\Support\Facades\Validator::make($args, [
        'data.*.password' => 'string|nullable|same:data.*.password_confirmation',
    ])->validate();
}

public function validationErrorMessages(array $args = []): array
{
    return [
        'name.ase enter your email address',
        'email.email' => 'Please enter a valid email address',
        'email.exists' => 'Sorry, this email address is already in use',
    ];
}

public function validationAttributes(array $args = []): array
{
    return [
        'email' => 'email address',
    ];
}

class RecipientInput extends InputType
{
    protected $attributes = ['name' => 'RecipientInput'];

    public function fields(): array
    {
        return [
            'createParams' => [
                'type' => Type::string(),
                'rules' => ['nullable', 'prohibits:mintParams'],
            ],
            'mintParams' => [
                'type' => Type::string(),
                'rules' => ['nullable', 'prohibits:createParams'],
            ],
        ];
    }
}

public function args(): array
{
    return [
        'recipients' => [
            'type' => Type::nonNull(Type::listOf(Type::nonNull(GraphQL::type('RecipientInput')))),
        ],
    ];
}

class MyMutation extends Mutation
{
    protected function processCollectedRules(array $rules): array
    {
        return $rules; // disable automatic cross-field rule prefixing
    }
}

declare(strict_types = 1);
namespace App\GraphQL\Queries;

use App\Models\User;
use Rebing\GraphQL\Support\Facades\GraphQL;
use GraphQL\Type\Definition\Type;
use GraphQL\Type\Definition\ResolveInfo;
use Rebing\GraphQL\Support\Query;
use SomeClassNamespace\SomeClassThatDoLogging;

class UsersQuery extends Query
{
    protected $attributes = [
        'name' => 'users',
    ];

    public function type(): Type
    {
        return Type::listOf(GraphQL::type('User'));
    }

    public function args(): array
    {
        return [
            'id' => [
                'type' => Type::string(),
            ]
        ];
    }

    public function resolve($root, array $args, $context, ResolveInfo $info, SomeClassThatDoLogging $logging)
    {
        $logging->log('fetched user');

        $users = User::all();

        return $users->get();
    }
}

declare(strict_types = 1);
namespace App\GraphQL\Middleware;

use Closure;
use GraphQL\Type\Definition\ResolveInfo;
use Illuminate\Pagination\Paginator;
use Rebing\GraphQL\Support\Middleware;

class ResolvePage extends Middleware
{
    public function handle($root, array $args, $context, ResolveInfo $info, Closure $next)
    {
        Paginator::currentPageResolver(function () use ($args) {
            return $args['pagination']['page'] ?? 1;
        });

        return $next($root, $args, $context, $info);
    }
}

declare(strict_types = 1);
namespace App\GraphQL\Queries;

use App\GraphQL\Middleware;
use Rebing\GraphQL\Support\Query;

class UsersQuery extends Query
{
    protected $middleware = [
        Middleware\Logstash::class,
        Middleware\ResolvePage::class,
    ];
}

declare(strict_types = 1);
namespace App\GraphQL\Queries;

use App\GraphQL\Middleware;
use Rebing\GraphQL\Support\Query as BaseQuery;

abstract class Query extends BaseQuery
{
    protected $middleware = [
        Middleware\Logstash::class,
        Middleware\ResolvePage::class,
    ];
}

    protected function getMiddleware(): array
    {
        return array_merge([...], $this->middleware);
    }
  
return [
    ...
    'resolver_middleware_append' => [YourMiddleware::class],
];

    ...
    public function boot()
    {
        ...
        GraphQL::appendGlobalResolverMiddleware(YourMiddleware::class);
        // Or with new instance
        GraphQL::appendGlobalResolverMiddleware(new YourMiddleware(...));
    }

GraphQL::prependGlobalResolverMiddleware(YourOutermostMiddleware::class);

declare(strict_types = 1);
namespace App\GraphQL\Middleware;

use Countable;
use GraphQL\Language\Printer;
use GraphQL\Type\Definition\ResolveInfo;
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
use Illuminate\Pagination\AbstractPaginator;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Config;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Route;
use Rebing\GraphQL\Support\Middleware;

class Logstash extends Middleware
{
    public function terminate($field, array $args, $context, ResolveInfo $info, $result): void
    {
        Log::channel('logstash')->info('', (
            collect([
                'query' => $info->fieldName,
                'operation' => $info->operation->name->value ?? null,
                'type' => $info->operation->operation,
                'fields' => array_keys(Arr::dot($info->getFieldSelection($depth = PHP_INT_MAX))),
                'schema' => Arr::first(Route::current()->parameters()) ?? Config::get('graphql.default_schema', 'default'),
                'vars' => $this->formatVariableDefinitions($info->operation->variableDefinitions),
            ])
                ->when($result instanceof Countable, function ($metadata) use ($result) {
                    return $metadata->put('count', $result->count());
                })
                ->when($result instanceof AbstractPaginator, function ($metadata) use ($result) {
                    return $metadata->put('per_page', $result->perPage());
                })
                ->when($result instanceof LengthAwarePaginator, function ($metadata) use ($result) {
                    return $metadata->put('total', $result->total());
                })
                ->merge($this->formatArguments($args))
                ->toArray()
        ));
    }

    private function formatArguments(array $args): array
    {
        return collect($args)
            ->mapWithKeys(function ($value, $key) {
                return ["\${$key}" => $value];
            })
            ->toArray();
    }

    private function formatVariableDefinitions(?iterable $variableDefinitions = []): array
    {
        return collect($variableDefinitions)
            ->map(function ($def) {
                return Printer::doPrint($def);
            })
            ->toArray();
    }
}

declare(strict_types = 1);
namespace App\GraphQL\Queries;

use Illuminate\Support\Facades\Auth;
use GraphQL\Type\Definition\ResolveInfo;

class UsersQuery extends Query
{
    public function authorize($root, array $args, $ctx, ResolveInfo $resolveInfo = null): bool
    {
        // true, if logged in
        return ! Auth::guest();
    }

    // ...
}

declare(strict_types = 1);
namespace App\GraphQL\Queries;

use Illuminate\Support\Facades\Auth;
use GraphQL\Type\Definition\ResolveInfo;

class UsersQuery extends Query
{
    public function authorize($root, array $args, $ctx, ResolveInfo $resolveInfo = null): bool
    {
        if (isset($args['id'])) {
            return Auth::id() == $args['id'];
        }

        return true;
    }

    // ...
}

declare(strict_types = 1);
namespace App\GraphQL\Queries;

use Illuminate\Support\Facades\Auth;
use GraphQL\Type\Definition\ResolveInfo;

class UsersQuery extends Query
{
    public function authorize($root, array $args, $ctx, ResolveInfo $resolveInfo = null): bool
    {
        if (isset($args['id'])) {
            return Auth::id() == $args['id'];
        }

        return true;
    }

    public function getAuthorizationMessage(): string
    {
        return 'You are not authorized to perform this action';
    }

    // ...
}

declare(strict_types = 1);
namespace App\GraphQL\Concerns;

use Illuminate\Support\Facades\Auth;

trait RequiresAuthentication
{
    public function authorize($root, array $args, $ctx): bool
    {
        return !Auth::guest();
    }
}

class UsersQuery extends Query
{
    use \App\GraphQL\Concerns\RequiresAuthentication;

    // ...
}

use Illuminate\Support\Facades\Auth;

class UserType extends GraphQLType
{
    // ...

    public function fields(): array
    {
        return [
            'id' => [
                'type'          => Type::nonNull(Type::string()),
                'description'   => 'The id of the user',
            ],
            'email' => [
                'type'          => Type::string(),
                'description'   => 'The email of user',
                'privacy'       => function (mixed $root, array $args, $ctx): bool {
                    // Only the authenticated user can see their own email.
                    // $root is the User model being resolved.
                    // $ctx is the query context value (see notes below).
                    // By default, AddAuthUserContextValueMiddleware sets
                    // $ctx to the authenticated user model directly.
                    return $root->id === Auth::id();
                },
            ],
        ];
    }

    // ...
}

use Illuminate\Support\Facades\Auth;
use GraphQL\Type\Definition\ResolveInfo;
use Rebing\GraphQL\Support\Privacy;

class MePrivacy extends Privacy
{
    public function validate(mixed $root, array $fieldArgs, mixed $queryContext = null, ?ResolveInfo $resolveInfo = null): bool
    {
        return $root->id === Auth::id();
    }
}

use MePrivacy;

class UserType extends GraphQLType
{
    // ...

    public function fields(): array
    {
        return [
            'id' => [
                'type'          => Type::nonNull(Type::string()),
                'description'   => 'The id of the user',
            ],
            'email' => [
                'type'          => Type::string(),
                'description'   => 'The email of user',
                'privacy'       => MePrivacy::class,
            ],
        ];
    }

    // ...
}

'ssn' => [
    'type'    => Type::string(),
    'args'    => [
        'reason' => [
            'type' => Type::nonNull(Type::string()),
        ],
    ],
    'privacy' => function (mixed $root, array $args, $ctx): bool {
        // Only allow access when a valid reason is provided.
        return in_array($args['reason'] ?? '', ['legal', 'compliance']);
    },
],

declare(strict_types = 1);
namespace App\GraphQL\Fields;

use GraphQL\Type\Definition\Type;
use Rebing\GraphQL\Support\Field;

class PictureField extends Field
{
    protected $attributes = [
        'description'   => 'A picture',
    ];

    public function type(): Type
    {
        return Type::string();
    }

    public function args(): array
    {
        return [
            'width' => [
                'type' => Type::int(),
                'description' => 'The width of the picture'
            ],
            'height' => [
                'type' => Type::int(),
                'description' => 'The height of the picture'
            ]
        ];
    }

    protected function resolve($root, array $args)
    {
        $width = isset($args['width']) ? $args['width']:100;
        $height = isset($args['height']) ? $args['height']:100;

        return 'https://placehold.co/'.$width.'x'.$height;
    }
}

declare(strict_types = 1);
namespace App\GraphQL\Types;

use App\GraphQL\Fields\PictureField;
use App\Models\User;
use GraphQL\Type\Definition\Type;
use Rebing\GraphQL\Support\Type as GraphQLType;

class UserType extends GraphQLType
{
    protected $attributes = [
        'name'          => 'User',
        'description'   => 'A user',
        'model'         => User::class,
    ];

    public function fields(): array
    {
        return [
            'id' => [
                'type' => Type::nonNull(Type::string()),
                'description' => 'The id of the user'
            ],
            'email' => [
                'type' => Type::string(),
                'description' => 'The email of user'
            ],
            //Instead of passing an array, you pass a class path to your custom field
            'picture' => PictureField::class
        ];
    }
}

declare(strict_types = 1);
namespace App\GraphQL\Fields;

use GraphQL\Type\Definition\Type;
use Rebing\GraphQL\Support\Field;

class FormattableDate extends Field
{
    protected $attributes = [
        'description' => 'A field that can output a date in all sorts of ways.',
    ];

    public function __construct(array $settings = [])
    {
        $this->attributes = \array_merge($this->attributes, $settings);
    }

    public function type(): Type
    {
        return Type::string();
    }

    public function args(): array
    {
        return [
            'format' => [
                'type' => Type::string(),
                'defaultValue' => 'Y-m-d H:i',
                'description' => 'Defaults to Y-m-d H:i',
            ],
            'relative' => [
                'type' => Type::boolean(),
                'defaultValue' => false,
            ],
        ];
    }

    protected function resolve($root, array $args): ?string
    {
        $date = $root->{$this->getProperty()};

        if (!$date instanceof Carbon) {
            return null;
        }

        if ($args['relative']) {
            return $date->diffForHumans();
        }

        return $date->format($args['format']);
    }

    protected function getProperty(): string
    {
        return $this->attributes['alias'] ?? $this->attributes['name'];
    }
}

declare(strict_types = 1);
namespace App\GraphQL\Types;

use App\GraphQL\Fields\FormattableDate;
use App\Models\User;
use GraphQL\Type\Definition\Type;
use Rebing\GraphQL\Support\Type as GraphQLType;

class UserType extends GraphQLType
{
    protected $attributes = [
        'name'          => 'User',
        'description'   => 'A user',
        'model'         => User::class,
    ];

    public function fields(): array
    {
        return [
            'id' => [
                'type' => Type::nonNull(Type::string()),
                'description' => 'The id of the user'
            ],
            'email' => [
                'type' => Type::string(),
                'description' => 'The email of user'
            ],

            // You can simply supply an instance of the class
            'dateOfBirth' => new FormattableDate,

            // Because the constructor of `FormattableDate` accepts our the array of parameters,
            // we can override them very easily.
            // Imagine we want our field to be called `createdAt`, but our database column
            // is called `created_at`:
            'createdAt' => new FormattableDate([
                'alias' => 'created_at',
            ])
        ];
    }
}

declare(strict_types = 1);
namespace App\GraphQL\Loaders;

use App\Models\User;
use GraphQL\Deferred;

class UserLoader
{
    /** @var list<int> */
    private array $pendingIds = [];

    /** @var array<int,User> */
    private array $loaded = [];

    /**
     * Register a key to be loaded and return a deferred resolver.
     */
    public function load(int $id): Deferred
    {
        $this->pendingIds[] = $id;

        return new Deferred(function () use ($id): ?User {
            $this->loadPending();

            return $this->loaded[$id] ?? null;
        });
    }

    /**
     * Bulk-fetch all pending keys in a single query.
     */
    private function loadPending(): void
    {
        $ids = array_diff(array_unique($this->pendingIds), array_keys($this->loaded));
        $this->pendingIds = [];

        if ($ids === []) {
            return;
        }

        $users = User::whereIn('id', $ids)->get()->keyBy('id');

        foreach ($users as $id => $user) {
            $this->loaded[$id] = $user;
        }
    }
}

// AppServiceProvider::register()

$this->app->scoped(\App\GraphQL\Loaders\UserLoader::class);

declare(strict_types = 1);
namespace App\GraphQL\Types;

use App\GraphQL\Loaders\UserLoader;
use App\Models\Post;
use GraphQL\Type\Definition\Type;
use Rebing\GraphQL\Support\Facades\GraphQL;
use Rebing\GraphQL\Support\Type as GraphQLType;

class PostType extends GraphQLType
{
    protected $attributes = [
        'name' => 'Post',
        'description' => 'A blog post',
    ];

    public function fields(): array
    {
        return [
            'id' => [
                'type' => Type::nonNull(Type::int()),
            ],
            'title' => [
                'type' => Type::nonNull(Type::string()),
            ],
            'author' => [
                'type' => GraphQL::type('User'),
                'description' => 'The post author, loaded via dataloader',
                'resolve' => function (Post $post) {
                    return app(UserLoader::class)->load($post->author_id);
                },
            ],
        ];
    }
}

class UserType extends GraphQLType
{
    // ...

    public function fields(): array
    {
        return [
            // ...

            // JSON column containing all posts made by this user
            'posts' => [
                'type'          => Type::listOf(GraphQL::type('Post')),
                'description'   => 'A list of posts written by the user',
                // Tells SelectFields this is not an Eloquent relation
                // The value defaults to true
                'is_relation' => false
            ]
        ];
    }

    // ...
}

declare(strict_types = 1);
namespace App\GraphQL\Types;

use App\Models\User;
use GraphQL\Type\Definition\Type;
use Rebing\GraphQL\Support\Type as GraphQLType;

class UserType extends GraphQLType
{
    protected $attributes = [
        'name'          => 'User',
        'description'   => 'A user',
        'model'         => User::class,
    ];

    public function fields(): array
    {
        return [
            'id' => [
                'type' => Type::nonNull(Type::string()),
                'description' => 'The id of the user',
            ],
            'email' => [
                'type' => Type::string(),
                'description' => 'The email of user',
            ],
            'address' => [
                'type' => Type::string(),
                'description' => 'The address of user',
                'deprecationReason' => 'Deprecated due to address field split'
            ],
            'address_line_1' => [
                'type' => Type::string(),
                'description' => 'The address line 1 of user',
            ],
            'address_line_2' => [
                'type' => Type::string(),
                'description' => 'The address line 2 of user',
            ],
        ];
    }
}

'defaultFieldResolver' => [Your\Klass::class, 'staticMethod'],

declare(strict_types = 1);
namespace App\Providers;

use GraphQL\Type\Definition\Type;
use Illuminate\Support\ServiceProvider;
use Rebing\GraphQL\Support\Facades\GraphQL;

class AppServiceProvider extends ServiceProvider
{
    /**
     * Bootstrap any application services.
     *
     * @return void
     */
    public function boot()
    {
        GraphQL::macro('listOf', function (string $name): Type {
            return Type::listOf(GraphQL::type($name));
        });
    }
}

'tracing' => [
    'driver' => \Rebing\GraphQL\Support\Tracing\OpenTelemetryTracingDriver::class,
    'driver_options' => [
        // Include the GraphQL document in spans (may contain sensitive data)
        '

'tracing' => [
    'driver' => \Rebing\GraphQL\Support\Tracing\OpenTelemetryTracingDriver::class,
    'field_tracing' => true,
],

'schemas' => [
    'internal' => [
        'query' => [/* ... */],
        'tracing' => false, // no tracing for this schema
    ],
],

// Global: tracing disabled
'tracing' => [
    'driver' => null,
],

'schemas' => [
    'default' => [
        'query' => [/* ... */],
        // No 'tracing' key - inherits global (disabled)
    ],
    'monitored' => [
        'query' => [/* ... */],
        'tracing' => [
            'driver' => \Rebing\GraphQL\Support\Tracing\OpenTelemetryTracingDriver::class,
            'field_tracing' => true,
        ],
    ],
],

// Global: tracing enabled, document excluded
'tracing' => [
    'driver' => \Rebing\GraphQL\Support\Tracing\OpenTelemetryTracingDriver::class,
    'driver_options' => [
        '       '

// config/graphql.php
'security' => [
    'disable_introspection' => env('GRAPHQL_DISABLE_INTROSPECTION', true),
],

'security' => [
    'query_max_depth' => 13, // default
],

'security' => [
    'query_max_complexity' => 500, // default
],

'posts' => [
    'type' => Type::listOf(GraphQL::type('Post')),
    'complexity' => fn (int $childCost, array $args): int => $childCost * ($args['limit'] ?? 10),
],

// config/graphql.php
'execution_middleware' => [
    Rebing\GraphQL\Support\ExecutionMiddleware\ValidateOperationParamsMiddleware::class,
    Rebing\GraphQL\Support\ExecutionMiddleware\AutomaticPersistedQueriesMiddleware::class,
    Rebing\GraphQL\Support\ExecutionMiddleware\ReadOnlyOperationMiddleware::class,
    Rebing\GraphQL\Support\ExecutionMiddleware\AddAuthUserContextValueMiddleware::class,
],

use Rebing\GraphQL\Support\Middleware\CsrfGuard;

// config/graphql.php — apply to all schemas:
'route' => [
    'middleware' => [CsrfGuard::class],
],

// Or per-schema with custom options:
'schemas' => [
    'admin' => [
        'middleware' => [CsrfGuard::class], // strict defaults
    ],
    'public' => [
        'middleware' => [
            CsrfGuard::using(strictWhenAmbiguous: false), // allow non-browser clients
        ],
    ],
],

// config/graphql.php
'security' => [
    'disable_introspection' => env('GRAPHQL_DISABLE_INTROSPECTION', true),
    'query_max_depth' => 13,
    'query_max_complexity' => 500,
],

'batching' => [
    'enable' => false,
],

// config/graphql.php

// Receives each GraphQL\Error\Error; must return an array
'error_formatter' => [App\GraphQL\ErrorFormatter::class, 'format'],

// Receives all errors + the formatter; must return an array of formatted errors
'errors_handler' => [App\GraphQL\ErrorHandler::class, 'handle'],

public function type(): Type
{
    return GraphQL::wrapType(
        'PostType',
        'PostMessageType',
        \App\GraphQL\Types\WrapMessagesType::class,
    );
}

public function resolve($root, array $args)
{
    return [
        'data' => Post::find($args['post_id']),
        'messages' => new Collection([
                new SimpleMessage("Congratulations, the post was found"),
                new SimpleMessage("This post cannot be edited", "warning"),
        ]),
    ];
}

namespace Tests\Feature;

use Tests\TestCase;

class BooksQueryTest extends TestCase
{
    public function test_can_query_books(): void
    {
        $response = $this->postJson('/graphql', [
            'query' => '{ books { id title author } }',
        ]);

        $response->assertOk()
            ->assertJsonStructure([
                'data' => [
                    'books' => [
                        '*' => ['id', 'title', 'author'],
                    ],
                ],
            ]);
    }
}

public function test_can_fetch_user_by_id(): void
{
    $response = $this->postJson('/graphql', [
        'query' => 'query FetchUser($id: String!) { user(id: $id) { id email } }',
        'variables' => ['id' => '1'],
    ]);

    $response->assertOk()
        ->assertJsonPath('data.user.id', '1');
}

public function test_can_update_user_password(): void
{
    $response = $this->postJson('/graphql', [
        'query' => 'mutation { updateUserPassword(id: "1", password: "newpassword") { id } }',
    ]);

    $response->assertOk()
        ->assertJsonPath('data.updateUserPassword.id', '1');
}

public function test_user_schema_n('/graphql/user', [
        'query' => '{ profile { id email } }',
    ]);

    $response->assertUnauthorized();
}

public function test_authorization_rejects_guest(): void
{
    $response = $this->postJson('/graphql', [
        'query' => '{ protectedQuery { id } }',
    ]);

    $response->assertOk()
        ->assertJsonPath('errors.0.message', 'Unauthorized');
}

public function test_validation_returns_errors(): void
{
    $response = $this->postJson('/graphql', [
        'query' => 'mutation { updateUserEmail(id: "", email: "not-an-email") { id } }',
    ]);

    $response->assertOk()
        ->assertJsonPath('errors.0.message', 'validation')
        ->assertJsonStructure([
            'errors' => [
                ['extensions' => ['validation']],
            ],
        ]);
}
bash
php artisan vendor:publish --provider="Rebing\GraphQL\GraphQLServiceProvider"

config/graphql.php
bash
php artisan make:graphql:query BooksQuery
bash
php artisan serve
sh
php artisan make:graphql:middleware ResolvePage
graphql
mutation test($value:ID) {
  someMutation(type:"falbala", optional_id: $value)
}
json5
{
  // Ops! typo in `values`
  "values": "138"
}