In the previous blog, we explored the key differences between REST API and GraphQL – what makes GraphQL exciting, and why it’s gaining much attention among modern developers.
In this tutorial, we’ll take the next big step: Implement GraphQL in Laravel using the powerful Lighthouse package. This guide will walk you through the setup, default schema testing, and building custom queries and mutations with resolvers.
If you’re getting started with GraphQL in Laravel, buckle up – this is the Laravel GraphQL example you’ve been looking for.
Create a Laravel project
- PHP 8+
- Laravel 9 or higher
Create a Laravel project
composer create-project laravel/laravel graphql-demo
Install the lighthouse package
composer require nuwave/lighthouse
Publish the Schema & Config Files
Publish the default GraphQL schema:
php artisan vendor:publish –tag=lighthouse-schema
Publish the configuration file:
php artisan vendor:publish –tag=lighthouse-config
Configure the GraphQL Endpoint
Go to config/lighthouse.php file and locate the route array. The default URI is:
'uri' => '/graphql'
You can change this to something like /api/graphql, or any of your choice. But for this tutorial, we’ll use the default.
Explore the Default Schema
After publishing, a new graphql/ folder will appear in your root directory. Inside, you’ll find a file named schema.graphql.
It includes a default type and query for the User model. Let’s test this out by seeding some dummy users.
Seed the Database with Users
php artisan make:seeder UserTableSeeder
Update the run() method in the seeder:
public function run(): void
{
$userData = [
[
'name' => 'John Doe',
'email' => 'johndoe@gmail.com',
'password' => Hash::make('password'),
],
[
'name' => 'Harry Doe',
'email' => 'harrydoe@gmail.com',
'password' => Hash::make('password'),
],
[
'name' => 'Kate Doe',
'email' => 'katedoe@gmail.com',
'password' => Hash::make('password'),
],
[
'name' => 'Emily Doe',
'email' => 'emilydoe@gmail.com',
'password' => Hash::make('password'),
],
];
foreach ($userData as $user) {
User::create($user);
}
}
Run the seeder:
php artisan db:seed –class=UserTableSeeder
Test the Default Queries
You can now test the default schema using any GraphQL client. I prefer Altair, but you can also use GraphiQL, Insomnia, or Postman.
Try the following queries for testing:
query user{
user(id: "1")
{
id
name
email
}
}query Users {
users {
data {
name
email
}
paginatorInfo {
total
currentPage
lastPage
}
}
}See how powerful this is? You didn’t have to write a single controller or route. Just define the schema, and you’re good to go.
But if you do want to customize logic — let’s say you want more control — you can build custom resolvers.
Create a Post Model with Migration
Let’s create a new model Post and add a relationship to the user
php artisan make:model Post -m
In the migration file:
public function up(): void
{
Schema::create('posts', function (Blueprint $table) {
$table->id();
$table->string('title');
$table->string('description');
$table->unsignedBigInteger('added_by');
$table->boolean('status')->default(false);
$table->timestamps();
$table->foreign('added_by')->references('id')->on('users')->onDelete('cascade');
});
}
Run the migration:
php artisan migrate
Define the relationship in the Post model:
public function user():BelongsTo
{
return $this->belongsTo(User::class,'added_by');
}
Define a Custom Mutation
Update schema.graphql
type Post{
id: ID!
title: String!
description: String!
added_by: ID!
status: Boolean!
}
type Mutation {
createPost(input: PostInput!): Post @field(resolver: "App\\GraphQL\\Mutations\\PostMutation@store")
}
input PostInput{
title: String!
description: String!
status: Boolean
}
Generate the mutation class:
php artisan lighthouse:mutation PostMutation
It will create a new file as: app/GraphQL/Mutations/PostMutation.php
Inside PostMutation.php, add store method as:
public function store($_, array $args)
{
$input = $args['input'];
$post = Post::create([
'title' => $input['title'],
'description' => $input['description'],
'added_by' => 1, // the id of authenticated user
'status' => $input['status'],
]);
$post->load('user');
return $post;
}
Now you can test the mutation as follows:
mutation createPost{
createPost(input:{
title: "My new post"
description:"This is the description"
status: true
}){
title
description
status
user{
id
name
email
}
}
}
The output will look something like this:
Define Custom Query Resolvers
In your schema.graphql
type Query {
post(id: ID! @eq): Post @find
posts(search: String): [Post!]! @field (resolver:"App\\GraphQL\\Queries\\PostQuery@getActivePosts")
}
Generate the query class:
php artisan lighthouse:query PostQuery
This will create a new class as app/GraphQL/Queries/PostQuery.php
In the PostQuery.php file, add the following
public function getActivePosts($_, array $args)
{
$query = Post::with('user')->where('status',true);
if($args['search'])
{
$posts = $query->where('title', 'LIKE', '%'.$args['search'].'%');
}
$posts = $query->latest()->get();
return $posts;
}
Test the query as follows:
query post{
post(id: 1){
title
description
status
user{
name
}
}
}query posts{
posts(search: "first"){
id
title
description
status
user{
name
}
}
}Wrapping Up
And there you go! You now have a fully functional Laravel GraphQL API in Laravel using Lighthouse.
With just a few commands and schema definitions, you can:
- Query data with precision
- Avoid over-fetching
- Add custom logic via resolvers
- Keep your backend clean and scalable
And the best part? You’ve done this using GraphQL in Laravel, a truly modern combo.
You can now expand further with:
- @auth / @guard for role-based access
- Custom middleware
- Enum types
- Custom directives
- Connect a Laravel GraphQL client like Apollo or urql for seamless frontend integration
- Connect GraphQL with Laravel MySQL databases directly
For more info, check out the official Lighthouse docs.
Conclusion
REST APIs still have their place, but when it comes to fine-grained control, developer speed, and frontend happiness — GraphQL in Laravel shines bright.
Whether you’re working on enterprise-level apps or just starting with your Laravel GraphQL example, the stack is flexible, clean, and super fun to use.
Give it a try, and happy coding!