Weave Code
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

Graphql Laravel Laravel Package

rebing/graphql-laravel

Code-first GraphQL integration for Laravel based on webonyx/graphql-php. Define schemas, types, queries and mutations in PHP. Supports multiple schemas, per-schema middleware, resolver middleware, and n+1 prevention via dataloaders or SelectFields eager loading.

View on GitHub
Deep Wiki
Context7
## Getting Started

### Minimal Setup
1. **Installation**:
   ```bash
   composer require rebing/graphql-laravel
   php artisan vendor:publish --provider="Rebing\GraphQL\GraphQLServiceProvider"

Configure config/graphql.php (default schema, routes, middleware).

  1. First Query:

    php artisan make:graphql:query GetUser
    php artisan make:graphql:type UserType
    

    Register in config/graphql.php:

    'schemas' => [
        'default' => [
            'query' => [App\GraphQL\Queries\GetUser::class],
            'types' => [App\GraphQL\Types\UserType::class],
        ],
    ],
    
  2. Test Endpoint:

    curl -X POST -H "Content-Type: application/json" \
      -d '{"query": "{ user(id: 1) { id name email } }"}' \
      http://localhost:8000/graphql
    

First Use Case: Fetch Eloquent Model

// app/GraphQL/Queries/GetUser.php
public function resolve($root, array $args) {
    return User::findOrFail($args['id']);
}

Key: Use SelectFields for eager loading:

use Rebing\GraphQL\Support\Facades\SelectFields;

public function resolve($root, array $args) {
    return SelectFields::resolve(
        User::findOrFail($args['id']),
        $this->getSelectFields(),
        UserType::class
    );
}

Implementation Patterns

1. Schema Organization

  • Multi-schema: Define per-schema routes, middleware, and types:
    'schemas' => [
        'admin' => [
            'query' => [AdminQuery::class],
            'middleware' => [AdminAuthMiddleware::class],
        ],
        'public' => [...],
    ],
    
  • Route Attributes: Use #[GraphQLSchema('admin')] on controllers.

2. Data Loading Strategies

Strategy Use Case Example
Dataloaders Batch DB queries (n+1 problem) UserLoader::batchLoadByIds()
SelectFields Eloquent eager loading SelectFields::resolve($model)

Example: Dataloader Integration

// app/GraphQL/Types/UserType.php
public function fields() {
    return [
        'posts' => [
            'type' => Type::listOf(PostType::class),
            'resolve' => fn($user) => $this->loaders->get(UserLoader::class)->load($user->id),
        ],
    ];
}

3. Resolver Workflow

  1. Default Resolver: Auto-discover resolve{FieldName}Field():
    public function resolveAuthorField() {
        return $this->author; // Eloquent relationship
    }
    
  2. Custom Resolver: Override resolve():
    public function resolve($root, array $args) {
        return User::with(['posts' => fn($q) => $q->where('published', true)])
                   ->find($args['id']);
    }
    

4. Validation

  • Inline Rules:
    public function args() {
        return [
            'email' => [
                'type' => Type::nonNull(Type::string()),
                'rules' => ['required', 'email'],
            ],
        ];
    }
    
  • Global Validation: Use rules() method:
    public function rules() {
        return [
            'email' => ['required', 'email'],
            'password' => ['required', 'min:8'],
        ];
    }
    

5. Middleware Patterns

  • Resolver Middleware (per-field):
    // app/GraphQL/Middleware/LogResolver.php
    public function handle($root, $args, $context, $info, Closure $next) {
        Log::info("Resolving {$info->parentType->name}.{$info->fieldName}");
        return $next($root, $args, $context, $info);
    }
    
    Register in config/graphql.php:
    'resolver_middleware_append' => [App\GraphQL\Middleware\LogResolver::class],
    
  • Execution Middleware (global):
    // app/GraphQL/Middleware/Execution/ValidateSchema.php
    public function handle($schemaName, Schema $schema, OperationParams $params, $root, $context, Closure $next) {
        if (config('graphql.disable_introspection') && $params->getQuery() === introspectionQuery()) {
            throw new \RuntimeException('Introspection disabled');
        }
        return $next($schemaName, $schema, $params, $root, $context);
    }
    

6. Testing

  • HTTP Client:
    $response = $this->httpGraphql('{ user(id: 1) { name } }');
    $response->assertJson(['data' => ['user' => ['name' => 'John']]]);
    
  • Schema Assertions:
    $this->assertSchemaHasType(UserType::class);
    $this->assertSchemaHasQuery(GetUser::class);
    

Gotchas and Tips

Pitfalls

  1. N+1 Queries:

    • Gotcha: Forgetting SelectFields or Dataloaders for nested relationships.
    • Fix: Always use SelectFields::resolve() for Eloquent models or Dataloader for custom queries.
    • Example:
      // ❌ Bad (n+1)
      public function resolvePosts() { return $this->posts; }
      
      // ✅ Good (eager load)
      public function resolvePosts() {
          return SelectFields::resolve($this->posts, $this->getSelectFields(), PostType::class);
      }
      
  2. Circular Dependencies:

    • Gotcha: Types referencing each other (e.g., UserPostUser).
    • Fix: Use lazy() for circular references:
      'posts' => [
          'type' => Type::listOf(PostType::class),
          'resolve' => fn($user) => $user->posts->load('user'), // Avoid circular load
      ],
      
  3. Introspection:

    • Gotcha: Disabled by default in production (GRAPHQL_DISABLE_INTROSPECTION=true).
    • Fix: Enable during development:
      GRAPHQL_DISABLE_INTROSPECTION=false
      
  4. Query Depth:

    • Gotcha: Unbounded recursion (e.g., user.posts.comments.user.posts...).
    • Fix: Configure depth limits in config/graphql.php:
      'query_depth_limit' => 10,
      
  5. Type Registration:

    • Gotcha: Forgetting to register types in config/graphql.php.
    • Fix: Use GraphQL::type('User') in resolvers to auto-register.

Debugging Tips

  1. Enable Tracing:

    'tracing' => [
        'driver' => 'opentelemetry',
        'enabled' => env('GRAPHQL_TRACING_ENABLED', false),
    ],
    
    • View traces in OpenTelemetry backend (e.g., Jaeger).
  2. Log Resolvers: Add middleware to log resolver calls:

    // app/GraphQL/Middleware/LogResolver.php
    public function handle($root, $args, $context, $info, Closure $next) {
        Log::debug("Resolving {$info->parentType->name}.{$info->fieldName}");
        return $next($root, $args, $context, $info);
    }
    
  3. Query Variables:

    • Gotcha: Forgetting to pass variables in the request.
    • Fix: Use variables in the request body:
      {
        "query": "{ user(id: $id) { name } }",
        "variables": { "id": 1 }
      }
      
  4. Error Handling:

    • Gotcha: Silent failures due to unhandled exceptions.
    • Fix: Customize error formatting:
      'error_formatter' => App\GraphQL\ErrorFormatter::class,
      

Extension Points

  1. Custom Scalars:
    // app/GraphQL/Scalars/JsonScalar.php
    class JsonScalar extends ScalarType {
        public function serialize($value) {
            return json_encode($value);
        }
        public function parseValue($value) {
            return json_decode($value, true);
        }
        public function parseLiteral(ast\Node $valueNode) {
            return json_decode($valueNode->value, true
    
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.
davejamesmiller/laravel-breadcrumbs
artisanry/parsedown
christhompsontldr/phpsdk
enqueue/dsn
bunny/bunny
enqueue/test
enqueue/null
enqueue/amqp-tools
bower-asset/punycode
bower-asset/inputmask
bower-asset/jquery
bower-asset/yii2-pjax
laravel/nova
spatie/laravel-mailcoach
spatie/laravel-superseeder
laravel/liferaft
nst/json-test-suite
danielmiessler/sec-lists
jackalope/jackalope-transport
twbs/bootstrap4