A hands-on start without resolver boilerplate
Lighthouse wires GraphQL schemas directly onto Eloquent models, without requiring a dedicated resolver for every field. Instead of building PHP classes, SDL directives like @hasMany, @paginate and @guard describe the entire connection to the database, relationships and middleware. Anyone already familiar with Laravel conventions gets productive with Lighthouse quickly.
Table of contents
- 1. What Lighthouse is and why GraphQL in Laravel
- 2. Installation and base configuration
- 3. Defining the schema with SDL directives
- 4. Using Eloquent models directly in the schema
- 5. Mutations and validation with Lighthouse
- 6. Authentication and authorization
- 7. Solving N+1 problems with batchloading
- 8. Subscriptions in Lighthouse
- 9. Lighthouse vs. other Laravel GraphQL solutions
- 10. Summary
- 11. FAQ
1. What Lighthouse is and why GraphQL in Laravel
Lighthouse is the leading GraphQL package for Laravel and is built internally on graphql-php by Webonyx. The key difference from a manual graphql-php integration: Lighthouse wires the schema directly to Eloquent models, query builders and Laravel middleware through SDL directives, instead of requiring a dedicated resolver for every field. A simple field like products: [Product!]! @all automatically loads all products from the database, with zero PHP resolver code.
For Laravel teams already working with Eloquent, form requests and policies, Lighthouse significantly lowers the entry barrier to GraphQL, because nearly every familiar Laravel concept can be reused directly in the schema. The trade-off: you give up some low-level control compared to plain graphql-php, but gain a massive boost in development speed for typical CRUD-heavy APIs, which make up the bulk of endpoints in many Laravel projects.
2. Installation and base configuration
Installing Lighthouse happens via Composer, followed by publishing the default configuration and an initial schema skeleton through an Artisan command. Right after installation, a working /graphql endpoint already exists with a minimal schema you can extend step by step. For local development, it's worth adding laravel-graphql-playground or Nuwave's own GraphiQL integration to test queries directly in the browser.
The configuration file config/lighthouse.php matters: this is where you set the schema path, enabled middleware groups and the namespace for custom resolver classes. In existing Laravel APIs that already use REST routes, Lighthouse can run alongside them without friction, GraphQL and REST happily share the same database and the same Eloquent models.
# Install Lighthouse and publish the base configuration
composer require nuwave/lighthouse
php artisan vendor:publish --tag=lighthouse-schema
php artisan vendor:publish --tag=lighthouse-config
# Optional: interactive GraphQL IDE for local development
composer require mll-lab/laravel-graphiql --dev
php artisan vendor:publish --tag=graphiql-config
# Verify the endpoint responds
php artisan serve
curl -X POST http://localhost:8000/graphql \
-H "Content-Type: application/json" \
-d '{"query": "{ __typename }"}'
3. Defining the schema with SDL directives
The heart of Lighthouse is the central file graphql/schema.graphql, which combines plain GraphQL SDL with additional, Lighthouse-specific directives. Directives such as @all, @find, @paginate and @orderBy translate standard field queries directly into Eloquent query calls, without developers writing the underlying SQL logic themselves. For more complex cases that can't be expressed through a directive, @field lets you point at your own PHP resolver class.
This combination of declarative SDL and targeted PHP code is the actual core of Lighthouse: for eighty percent of fields, typical listings, detail lookups and simple filters, a single-line directive is enough. Only for domain-specific complex logic, for example a price calculation with multiple discount rules, is a dedicated resolver required. That keeps schema files considerably more compact than comparable schema-first setups built on plain graphql-php.
# schema.graphql — Lighthouse directives bind fields to Eloquent directly
type Product {
id: ID!
sku: String!
name: String!
price: Float!
category: Category! @belongsTo
reviews: [Review!]! @hasMany
}
type Category {
id: ID!
name: String!
products: [Product!]! @hasMany
}
type Query {
products(name: String @where(operator: "like")): [Product!]! @paginate(defaultCount: 20)
product(sku: String! @eq): Product @find
}
4. Using Eloquent models directly in the schema
Lighthouse detects relationships between GraphQL types and Eloquent models through naming conventions, much like Eloquent itself detects relationships between tables. Directives such as @belongsTo, @hasMany and @belongsToMany directly mirror the corresponding Eloquent relationship methods and automatically take over their eager-loading behavior, as long as they're correctly combined with batchloading. For fields that don't correspond directly to a database column, for example a computed discountedPrice, Lighthouse automatically falls back to a same-named accessor method on the Eloquent model.
This tight coupling to Eloquent is both the strength and the boundary of Lighthouse: for domains that map cleanly onto Eloquent models, like classic e-commerce or CMS structures, a lot of boilerplate disappears. For GraphQL schemas that deviate significantly from the database structure, for example aggregated reporting views spanning multiple tables, the direct directive binding hits its limits, and dedicated resolver classes become the better choice there.
5. Mutations and validation with Lighthouse
Mutations in Lighthouse are defined via SDL just like queries, usually combined with @create, @update or a custom resolver for more complex writes. For input validation, Lighthouse reuses the validation rules familiar from Laravel directly as the argument directive @rules, so rules such as required, min or your own custom rule classes can be reused, without maintaining separate GraphQL-specific validation logic.
If validation fails, Lighthouse automatically returns structured errors in the GraphQL errors array, including a validation object under extensions that lists the concrete error messages per field. Frontend clients can display form errors just as granularly as with classic Laravel form request validation over REST, a real advantage over hand-built validation solutions in plain graphql-php.
# schema.graphql — mutation with Laravel validation rules reused as-is
type Mutation {
createReview(
productId: ID! @rules(apply: ["required", "exists:products,id"])
rating: Int! @rules(apply: ["required", "integer", "min:1", "max:5"])
comment: String @rules(apply: ["nullable", "max:2000"])
): Review! @create
}
6. Authentication and authorization
For authentication, Lighthouse uses the same guards configured for REST routes in Laravel, usually Sanctum or Passport for API tokens. The @guard directive protects individual fields or entire mutation types and automatically rejects unauthenticated requests with a matching GraphQL error, before any resolver even runs. That eliminates manually checking auth status in every single resolver.
For fine-grained authorization, for example whether a user is allowed to edit a particular review, the @can directive taps into existing Laravel policy classes, the exact same ones used for controller actions. That reuse effect is one of the biggest practical benefits of Lighthouse: auth logic doesn't need to be implemented twice for REST and GraphQL, it lives in one central place in the Laravel project.
<?php
declare(strict_types=1);
namespace App\Policies;
use App\Models\Review;
use App\Models\User;
// Reused for both REST controllers and GraphQL @can directives
final class ReviewPolicy
{
public function update(User $user, Review $review): bool
{
return $user->id === $review->user_id || $user->hasRole('moderator');
}
}
7. Solving N+1 problems with batchloading
Without precautions, nested GraphQL queries over Eloquent relationships lead to the classic N+1 problem: a query for 50 products, each with a related category, naively produces 51 database queries. Lighthouse solves this with built-in batchloading, which activates automatically as soon as relationship directives like @belongsTo or @hasMany are used. Instead of resolving each relationship individually, Lighthouse collects all requested IDs within a query and loads them in a single, whereIn-bundled query.
For custom, non-standard resolvers that don't run through the built-in relationship directives, batchloading has to be implemented manually with the BatchLoader interface, which Lighthouse provides for exactly this purpose. Anyone writing complex custom resolvers and forgetting to account for batchloading quickly runs into performance problems that only become visible at larger data volumes, a common pitfall when moving from simple directives to custom resolver code.
8. Subscriptions in Lighthouse
Lighthouse supports GraphQL subscriptions for real-time updates, usually via Pusher or Laravel Echo as the transport layer, combined with Redis for distributing events across multiple server instances. A subscription is defined in the SDL similarly to a query, with the additional @subscription directive pointing to a PHP class that decides which connected clients should receive a given event.
In practice, subscriptions in Lighthouse work well for notifications and status updates, for example the processing status of an order, but they're no substitute for high-frequency real-time use cases like live chat with very many concurrent connections, where specialized WebSocket infrastructure is often the better choice. Laravel's broadcasting layer, which many projects already configure for other purposes, can be reused directly for Lighthouse subscriptions.
9. Lighthouse vs. other Laravel GraphQL solutions
Besides Lighthouse, there are other ways to wire GraphQL into Laravel, each with a different level of abstraction and scope of control.
| Approach | Eloquent integration | Control | Entry effort |
|---|---|---|---|
| Lighthouse (Nuwave) | Direct via SDL directives | Medium, custom resolvers where needed | Low |
| graphql-php directly | Manual in resolver code | Full | High |
| API Platform (Symfony) | Only sensible with Symfony/Doctrine | High, but Symfony-bound | Medium |
| Laravel + plain REST | Native Laravel conventions | Full | Low |
For Laravel teams already leaning heavily on Eloquent who need a working GraphQL API quickly, Lighthouse is, as a rule, the right choice. Only when a project needs very specific execution strategies that deviate from standard CRUD, for example custom query batching at the protocol level, does it pay off to reach for graphql-php directly, at the cost of correspondingly higher implementation effort.
Mironsoft
Laravel and GraphQL architecture for data-intensive applications
Building a GraphQL API for your Laravel project?
We set up Lighthouse, model your schema along your Eloquent structure, and use batchloading, clean validation and guard directives to deliver a performant, secure GraphQL API.
Schema design
Modeling SDL directives around existing Eloquent models and relationships
Performance
Implementing batchloading against N+1 problems in custom resolvers
Auth integration
Reusing guards and policies centrally across REST and GraphQL
10. Summary
Lighthouse drastically cuts the effort needed for a production-ready GraphQL API in Laravel by binding SDL directives directly to Eloquent models, validation rules and auth guards. For typical CRUD-heavy domains, almost all the resolver boilerplate a manual graphql-php integration would require simply disappears. Batchloading against N+1 problems already comes built in for standard relationships, custom resolvers need it implemented explicitly.
Teams already building in Laravel benefit most from being able to share validation rules, policies and guards between REST and GraphQL, instead of maintaining auth and validation logic twice. For projects with very specific execution requirements, reaching for graphql-php directly remains an option, but for the vast majority of Laravel use cases, Lighthouse is the more pragmatic and faster path.
Lighthouse GraphQL for Laravel — Key Takeaways
SDL directives
@all, @find, @paginate, @hasMany bind fields directly to Eloquent, no custom resolver code.
Validation & auth
@rules, @guard and @can reuse existing Laravel validation rules, guards and policies.
Batchloading
Automatic for standard relationship directives, manual via the BatchLoader interface for custom resolvers.
Subscriptions
Real-time updates over Pusher/Laravel Echo and Redis, well suited to notifications and status changes.