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

Graphql Laravel Select Fields Laravel Package

rebing/graphql-laravel-select-fields

Laravel package for rebing/graphql-laravel that adds select-field support to GraphQL queries, letting resolvers fetch only the Eloquent columns and relationships requested by clients. Reduce over-fetching, simplify field selection, and improve query performance.

View on GitHub
Deep Wiki
Context7

Getting Started

rebing/graphql-laravel wraps webonyx/graphql-php in Laravel's container, routing, config, and validation. You write schema classes; the package resolves them and serves a single (or multi) endpoint.

  1. Install and publish the config:

    composer require rebing/graphql-laravel
    php artisan vendor:publish --provider="Rebing\GraphQL\GraphQLServiceProvider" --tag=config
    

    Package discovery registers the provider and the GraphQL facade (Rebing\GraphQL\Support\Facades\GraphQL).

  2. Open config/graphql.php first — it is the real entry point. Confirm three keys before writing any code:

    • route.prefix (default graphql) and route.middleware
    • default_schema (default)
    • graphiql (dev-only GUI, default /graphiql)
  3. Generate a starter schema with the package's generators:

    php artisan make:graphql:type UserType
    php artisan make:graphql:query UserQuery
    php artisan make:graphql:mutation CreateUserMutation
    php artisan make:graphql:scalar DateTime
    
  4. Register the generated classes in the schemas.default block (query, mutation, types). Nothing is auto-discovered — an unregistered class is invisible to the schema.

  5. First use case: a read-only type + query. A Type declares $attributes['name'] and a fields() array; a Query declares args(), type(), and resolve(). POST to /graphql from /graphiql and you have a working endpoint.

  6. This 1.0.0 stable release is aligned with the 10.x line of the package. If you are coming from 9.x, diff your published config with php artisan vendor:publish --provider="Rebing\GraphQL\GraphQLServiceProvider" --tag=config --force before assuming defaults still apply, and check composer.json constraints — a major bump drops older Laravel/PHP combinations.

First verification: curl -X POST /graphql -d '{"query":"{ __typename }"}' should return JSON, not a 404 — if it 404s, the route prefix or middleware is the culprit, not your schema.

Implementation Patterns

Types as the unit of reuse. Types are registered by name and looked up lazily via the facade. Use GraphQL::type('User') rather than importing the class, because it respects the registry and avoids instantiating types that a given query never touches.

public function fields(): array
{
    return [
        'id'    => ['type' => Type::nonNull(Type::id())],
        'name'  => ['type' => Type::string()],
        'posts' => [
            'type'    => Type::listOf(GraphQL::type('Post')),
            'resolve' => fn ($user) => $user->posts()->published()->get(),
        ],
    ];
}

Resolvers on fields are how you keep the query layer thin — push relation logic into the type, not into every query that returns a user.

Query + args + validation in one class. Arguments carry rules, which the package turns into a Laravel validator. On failure it throws Rebing\GraphQL\Error\ValidationError, which the error formatter serialises into the errors array — not a 422.

public function args(): array
{
    return [
        'id' => ['name' => 'id', 'type' => Type::nonNull(Type::int()),
                 'rules' => ['required', 'integer', 'exists:users,id']],
    ];
}

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

Mutations return a payload type, not a scalar. Model the mutation result as its own Type (CreateUserPayload with user and errors) so clients can branch on domain errors without inspecting top-level GraphQL errors. Throw for exceptional cases, return structured errors for expected ones.

Multiple schemas. Declare schemas.public and schemas.admin in config; each gets its own query/mutation roots, types, and middleware. Select via the schema segment on the route (or GraphQL::schema('admin') in tests). This is the cleanest way to keep __schema introspection of internal mutations off the public endpoint.

Pagination. Use the bundled Rebing\GraphQL\Support\PaginationType (or SimplePaginationType) and return a LengthAwarePaginator from the resolver — the type maps data, total, per_page, current_page, last_page, next_page_url, from, to for you. Override via pagination_type if you need cursor-style pages instead.

Authorization in two layers. Coarse: route.middleware / schemas.*.middleware (auth:sanctum, can:viewAdmin). Fine: per-query/mutation authorize() or per-field 'authorize' => fn ($root, $args, $ctx) => ... in fields(). Do both — middleware protects the endpoint, field-level checks protect against a user crafting a query that traverses into someone else's data.

Error formatting. Point error_formatter at your own class to control what leaks to clients, and errors_handler at a reporter to forward exceptions to Sentry/Flare. Keep the GraphQL spec shape (errors[].message, .locations, .path, .extensions) and add machine-readable codes in extensions rather than stuffing them into message.

Testing. Skip HTTP for schema tests: GraphQL::query($query, $variables) returns the full result array and lets you assert on errors precisely. For integration, $this->postJson('/graphql', [...])->assertJsonPath('data.user.id', 1) — remember the status is 200 even on failure.

Custom scalars. Extend Scalar, implement serialize/parseValue/parseLiteral, register in types. Date handling is the classic case: serialise to ISO-8601 and parse back to Carbon in one place rather than in every resolver.

File uploads go through the bundled UploadType scalar with multipart/form-data; the resolver receives an UploadedFile-like object, so validation rules behave as usual.

Gotchas and Tips

  • config:cache and closures. Any closure left in config/graphql.php (especially types or middleware) breaks php artisan config:cache. Use class-string arrays and let the container build them.
  • Everything must be registered. GraphQL::type('Post') throws at schema-build time if Post is not in schemas.*.types or types. Circular references (User → Post → User) work fine because lookup is by name; class imports do not solve this.
  • HTTP status is always 200. Validation failures, auth failures, and exceptions all arrive as 200 with an errors array. Middleware that assumes 401/422 semantics will misbehave, and API clients that only check status codes will silently treat errors as success.
  • N+1 is the default. A posts field resolving $user->posts per row will issue one query per user. Either eager-load in the parent resolver (User::with('posts')) or wire a DataLoader (overblog/dataloader-php) into the GraphQL context. This is the most common performance regression in this package.
  • Guard the GraphiQL route. It is enabled by default and, depending on route.middleware, may be publicly reachable. Restrict it to local env or wrap it in auth, and consider setting security.disable_introspection on production schemas so clients cannot enumerate internal types.
  • Cap query cost. security.query_max_depth and security.query_max_complexity are your only defence against a hostile nested query. Set them; there is no automatic limit.
  • Auth context mismatches. The context passed to resolvers is whatever the controller builds (typically the request). If you authenticate via Sanctum in middleware but resolve ownership via auth()->user(), confirm the guard matches — auth:sanctum plus auth()->user() on the default session guard is a frequent source of "user is null in resolver".
  • Debug mode leaks internals. With APP_DEBUG=true the default formatter exposes exception messages and traces. Verify production output before shipping; the difference between "Internal error" and a full stack trace is one env var.
  • Config changes need a cache clear. Editing config/graphql.php in a cached environment appears to do nothing. Run php artisan config:clear when a newly registered type "does not exist".
  • Batched requests are positional. When posting an array of operations, errors come back indexed by position; zip by index, never by path. A partial failure still returns 200.
  • Extension points worth knowing: per-schema execution_method, schema-level middleware, the GraphQL::addType() runtime registry, and a custom defaultFieldResolver in config — the last is the lever for Eloquent attribute resolution if you want $user->name to work without declaring a resolver on every field.
  • Upgrade hygiene for the 10.x line: diff the published config, re-run generators if you depend on their stubs, and grep for any overridden ErrorFormatter/ErrorHandler signatures — those are the classes most likely to shift across a major.
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.
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
jinas/dhivatar-component
mltstephane/laravel-quality
c-point-labs/laravel-hostyourai
cgv/localized-resource
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox
pairus/product-data