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

Graphqlite Laravel Laravel Package

thecodingmachine/graphqlite-laravel

Seamlessly integrates GraphQLite with Laravel, letting you define GraphQL queries and mutations using PHP attributes, type-hinted classes, and dependency injection. Includes configuration, service provider wiring, and Laravel-friendly setup for building typed GraphQL APIs.

View on GitHub
Deep Wiki
Context7

Getting Started

What this package is: a thin Laravel service provider around thecodingmachine/graphqlite (GraphQLite). It does not add GraphQL features of its own — it wires GraphQLite's SchemaFactory into Laravel's container, registers a single GraphQL HTTP endpoint, publishes a config file, and adapts Laravel's auth guard to GraphQLite's security annotations. Everything you write day-to-day is plain GraphQLite (#[Query], #[Mutation], #[Type], #[Field], #[Input]).

Minimal setup

composer require thecodingmachine/graphqlite-laravel
php artisan vendor:publish --provider="GraphQLite\Laravel\Providers\GraphQLiteServiceProvider"
# or: php artisan vendor:publish --tag=graphqlite

Then open config/graphqlite.php and verify the four things that matter:

  1. Route — URI (typically /graphql) and middleware. Set this to ['api'] unless you deliberately need session/CSRF handling.
  2. Namespaces — the directories GraphQLite scans for annotated classes (e.g. App\GraphQL). Classes outside these are invisible.
  3. Security — which Laravel guard backs #[Logged] / #[Right].
  4. Debug — whether exception messages are exposed in the errors payload.

Confirm the endpoint exists:

php artisan route:list | grep -i graphql

First use case — create app/GraphQL/HelloQuery.php:

namespace App\GraphQL;

use GraphQLite\Annotations\Query;

class HelloQuery
{
    #[Query]
    public function hello(string $name = 'world'): string
    {
        return "Hello, {$name}";
    }
}

Then curl -X POST /graphql -H 'Content-Type: application/json' -d '{"query":"{ hello(name: \"Laravel\") }"}'.

Where to look first: the published config/graphqlite.php and the provider class itself (it is small). After that, read GraphQLite's own documentation — this package's README only covers installation, and 95% of the questions you will have ("how do I return a list?", "how do I mark a field nullable?") are GraphQLite questions, not Laravel ones. Note that GraphQLite v6 is attribute-only; if you are on an older GraphQLite v5, the syntax is Doctrine annotations instead and you need doctrine/annotations installed.

Implementation Patterns

One resolver class per domain, constructor-injected. The package adapts Laravel's container to GraphQLite's ContainerInterface, so constructor dependencies on repositories/services are resolved automatically. Keep queries and mutations in separate classes (PostQueries, PostMutations) rather than one god-class; schema introspection and testing get much easier.

class PostQueries
{
    public function __construct(private PostRepository $posts) {}

    #[Query]
    public function post(int $id): PostType
    {
        return PostType::fromModel($this->posts->findOrFail($id));
    }

    /** @return PostType[] */
    #[Query]
    public function posts(int $first = 20, int $offset = 0): array
    {
        return $this->posts->page($first, $offset)->map(fn ($p) => PostType::fromModel($p))->all();
    }
}

Prefer DTOs (#[Type] classes) over exposing Eloquent models directly. A #[Type] class with public properties or #[Field] methods gives you a stable schema independent of column renames, and lets you attach authorization/computed fields without polluting the model. Add #[Field] methods for relations:

#[Type]
class PostType
{
    #[Field]
    public function author(): UserType { /* ... */ }
}

Inputs and validation. Use #[Input] classes with promoted constructor properties for mutations. GraphQLite will not run Laravel's FormRequests or validate() automatically — call Validator::make($data, [...])->validate() inside the mutation (catching ValidationException and rethrowing as GraphQLException if you want clean messages), or move validation into a service/action class that both HTTP and GraphQL entry points share.

Auth: two layers, use both deliberately.

  • Coarse: put auth:sanctum (or your guard) in graphqlite.route.middleware. This rejects unauthenticated requests at the HTTP layer.
  • Fine: #[Logged] / #[Right('ROLE_ADMIN')] on individual fields. #[Logged] consults the configured Laravel guard; #[Right] requires a role concept. Since Laravel has no roles by default, most teams either (a) implement a custom authentication/security service bound in a service provider, or (b) skip #[Right] and call Gate::authorize('update', $post) inside the resolver. Option (b) usually fits Laravel idioms better.

Current user. Auth::user() inside a resolver works and is the least surprising option. If you want to avoid facades, inject Illuminate\Contracts\Auth\Factory into the query class. GraphQLite's Context object exists but the Laravel adapter does not populate it with anything you cannot get from Auth / the request.

Error handling. GraphQLite returns HTTP 200 with an errors array. Throw GraphQLite\Exceptions\GraphQLException for expected domain failures, or annotate the method with #[FailWith(...)] for typed errors. Reserve uncaught exceptions for real bugs — they will be masked in production if debug is off, which is what you want but complicates debugging.

N+1 and pagination are your responsibility. There is no dataloader. For list fields, eager-load with with() and batch related lookups with a single whereIn. Always give list arguments a first/limit with a sane maximum; an unbounded posts: [Post!]! is a DoS vector on a public endpoint. If you expose deep nesting, enable GraphQLite's query depth and complexity limits (or write a small middleware) before launch.

Testing. It is just an HTTP endpoint, so Laravel feature tests are the right tool:

$this->actingAs($user)->postJson('/graphql', [
    'query' => 'mutation($id: ID!) { deletePost(id: $id) }',
    'variables' => ['id' => $post->id],
])->assertJsonPath('data.deletePost', true);

Add one introspection test ({ __schema { types { name } } }) and snapshot it — it catches accidental schema breaks (renamed types, changed nullability) faster than resolver tests.

Deployment. Run php artisan config:cache and route:cache as usual. GraphQLite caches reflection metadata — if you use the Laravel cache store for that, remember php artisan cache:clear on deploy when the schema changed.

Gotchas and Tips

  • Version drift is the real risk. With 18 stars and a last release in May 2023, this package is effectively "done" rather than maintained. Before adopting, check composer.json for its GraphQLite constraint and its Laravel constraint. On Laravel 11 / newer GraphQLite versions, expect to either pin older dependencies or fork the provider. The good news: the provider is a few hundred lines. If it blocks you, copy GraphQLiteServiceProvider into app/Providers and drop the composer dependency — you lose nothing but vendor:publish.

  • CSRF bites immediately. If the route middleware includes web, a browser POST to /graphql returns 419. Either use api middleware with token auth, or add graphql to VerifyCsrfToken::$except (or send X-XSRF-TOKEN). Decide this before you build a frontend against the endpoint.

  • Namespace scanning is config-driven and cached. Adding a new directory under App\GraphQL\... requires it to be listed in config, and after changing config you need php artisan config:clear locally. In production, config:cache means the namespace list is frozen at deploy time — a class in an unlisted namespace simply won't appear in the schema, with no error.

  • Type name collisions. GraphQLite derives the GraphQL type name from the class short name. Two Post classes in different namespaces collide at schema build time. Use #[Type(name: "BlogPost")] early rather than after clients depend on the name.

  • #[Logged] can be "true" while Auth::user() is null. The annotation checks whatever guard is configured in graphqlite.security.guard; the route middleware may authenticate a different guard (e.g. Sanctum vs web). Align them, or you get a schema that says "logged in" while resolvers see an anonymous user.

  • Schema not updating after a code change? Check, in order: config cache, GraphQLite's reflection/annotation cache, and OPcache on the web server. This is the single most common "the package is broken" report.

  • Everything returns 200. GraphQL errors, validation failures, and authorization errors all come back as HTTP 200 with an errors body. Your uptime monitor, API gateway, and Laravel's exception reporting thresholds won't fire. Log GraphQL errors explicitly (a middleware around the controller, or a custom error handler) if you want alerting.

  • No built-in GraphiQL in most versions. Point Altair or the GraphiQL desktop app at the endpoint instead of expecting /graphql to render an IDE. If you need one in-app, add a view that loads GraphiQL and gate it behind a dev-only middleware.

  • File uploads need three things aligned: the GraphQL multipart request spec on the client, the package's upload handling/config, and PHP's upload_max_filesize / post_max_size. A silently dropped file is almost always PHP config, not the package.

  • Extension points (when the defaults don't fit): rebind GraphQLite\Container\ContainerInterface to customize how resolver classes are instantiated, rebind the authentication service to implement #[Right] against your own role system, and decorate or replace the SchemaFactory singleton in a service provider to add global middleware (logging, timing, complexity limits).

  • If you're choosing today: for a new Laravel project, weigh this against nuwave/lighthouse (SDL-first, much larger community) or hand-writing a controller that boots GraphQLite's SchemaFactory directly (about 30 lines — route, container binding, auth binding). The value this package adds is convenience, not capability, and that convenience has a maintenance tail.

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.
robertmarney/lara-hierarchial-collections
simonschaufi/rule-doc-generator
emissaoja/emissaoja
blackbricksoftware/gravityforms-civicrm-form-processor
al-rahabi/fancy-crud
jinas/dhivatar-component
mltstephane/laravel-quality
bernskiold/laravel-talentlms
bernskioldmedia/laravel-form-architect
youbar/easy-crud
c-point-labs/laravel-hostyourai
dbravoan/dbcms-admin-theme-dbcms
dbravoan/dbcms-theme-db-cyber
dbravoan/dbcms-blocks-basic
dbravoan/dbcms-module-content-audit
dbravoan/dbcms-module-ai-assistant
paygate-to/paygate-php
padosoft/askmydocs-connector-base
defstudio/supercharged-carbon
bernskioldmedia/laravel-matomo