Lighthouse GraphQL for Laravel: A Hands-On Start
AI generated
{ }
type
GraphQL · Laravel · Eloquent
Lighthouse GraphQL for Laravel
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.

18 min read Lighthouse · Laravel · Eloquent · Subscriptions graphql-php · PHP 8.x · Artisan

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.

11. FAQ: Lighthouse GraphQL for Laravel

1What is Lighthouse for Laravel?
The leading GraphQL package for Laravel, built on graphql-php, binding schemas via SDL directives to Eloquent, validation and auth guards.
2Does every field need a resolver?
No, directives like @all, @find and @paginate handle standard cases automatically. Custom resolvers only for complex business logic.
3How does mutation validation work?
Via @rules using the same rules as Laravel form requests. Errors land structured in the errors array with a validation object.
4How to protect fields from unauth access?
With @guard for auth guards and @can for fine-grained policies, both reused from the existing Laravel setup.
5Does Lighthouse solve N+1 automatically?
For standard relationship directives yes, via built-in batchloading. Custom resolvers need the manual BatchLoader interface.
6Does Lighthouse run alongside REST?
Yes, without friction. Both share the database, Eloquent models, validation and policies.
7Does Lighthouse support subscriptions?
Yes, via @subscription with Pusher or Laravel Echo as transport and Redis for distribution across server instances.
8When use graphql-php directly instead?
For very specific execution strategies beyond standard CRUD, for example custom protocol-level batching.
9How to test a Lighthouse schema?
With PHPUnit via the bundled GraphQL test trait, similar to Laravel feature tests for REST.
10Suitable for small projects?
Yes, low entry effort is an advantage there. For very simple APIs, plain REST can still be more pragmatic.