GraphQL offers a powerful alternative to REST APIs, letting clients request exactly the data they need. Lighthouse brings first-class GraphQL support to Laravel. At ZIRA Software, we use GraphQL for complex data requirements.
Installation
composer require nuwave/lighthouse
php artisan vendor:publish --tag=lighthouse-schema
Schema Definition
# graphql/schema.graphql
type Query {
users: [User!]! @paginate
user(id: ID! @eq): User @find
posts(published: Boolean @eq): [Post!]! @all
}
type Mutation {
createUser(input: CreateUserInput! @spread): User! @create
updateUser(id: ID!, input: UpdateUserInput! @spread): User! @update
deleteUser(id: ID!): User! @delete
}
type User {
id: ID!
name: String!
email: String!
posts: [Post!]! @hasMany
created_at: DateTime!
}
type Post {
id: ID!
title: String!
content: String!
published: Boolean!
author: User! @belongsTo
comments: [Comment!]! @hasMany
}
type Comment {
id: ID!
body: String!
user: User! @belongsTo
post: Post! @belongsTo
}
input CreateUserInput {
name: String!
email: String! @rules(apply: ["email", "unique:users"])
password: String! @hash
}
input UpdateUserInput {
name: String
email: String @rules(apply: ["email", "unique:users"])
}
Custom Resolvers
// app/GraphQL/Queries/UserStats.php
namespace App\GraphQL\Queries;
class UserStats
{
public function __invoke($rootValue, array $args)
{
return [
'total_users' => User::count(),
'active_users' => User::where('active', true)->count(),
'new_today' => User::whereDate('created_at', today())->count(),
];
}
}
# Add to schema
type Query {
userStats: UserStatsPayload! @field(resolver: "App\\GraphQL\\Queries\\UserStats")
}
type UserStatsPayload {
total_users: Int!
active_users: Int!
new_today: Int!
}
Mutations with Validation
// app/GraphQL/Mutations/CreatePost.php
namespace App\GraphQL\Mutations;
use App\Models\Post;
use Illuminate\Support\Facades\Auth;
class CreatePost
{
public function __invoke($rootValue, array $args)
{
$post = new Post();
$post->title = $args['title'];
$post->content = $args['content'];
$post->user_id = Auth::id();
$post->published = $args['published'] ?? false;
$post->save();
return $post;
}
}
type Mutation {
createPost(
title: String! @rules(apply: ["required", "min:3", "max:255"])
content: String! @rules(apply: ["required", "min:10"])
published: Boolean
): Post! @field(resolver: "App\\GraphQL\\Mutations\\CreatePost")
}
Authentication
# Schema with auth directives
type Query {
me: User @auth
myPosts: [Post!]! @auth @all(model: "Post", scopes: ["currentUser"])
}
type Mutation {
login(email: String!, password: String!): AuthPayload!
logout: LogoutPayload! @guard
}
type AuthPayload {
access_token: String!
token_type: String!
user: User!
}
// app/GraphQL/Mutations/Login.php
class Login
{
public function __invoke($rootValue, array $args)
{
$credentials = [
'email' => $args['email'],
'password' => $args['password'],
];
if (!Auth::attempt($credentials)) {
throw new AuthenticationException('Invalid credentials');
}
$user = Auth::user();
$token = $user->createToken('api')->plainTextToken;
return [
'access_token' => $token,
'token_type' => 'Bearer',
'user' => $user,
];
}
}
Querying from Client
// GraphQL query
const GET_USER_WITH_POSTS = `
query GetUser($id: ID!) {
user(id: $id) {
id
name
email
posts {
id
title
published
}
}
}
`;
// Fetch
const response = await fetch('/graphql', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + token,
},
body: JSON.stringify({
query: GET_USER_WITH_POSTS,
variables: { id: '1' }
}),
});
const { data } = await response.json();
console.log(data.user);
N+1 Query Prevention
# Use @with to eager load
type User {
posts: [Post!]! @hasMany @with(relation: "posts")
}
# Or batch loading with dataloader
type Post {
author: User! @belongsTo
}
Conclusion
GraphQL with Lighthouse provides flexible, efficient APIs for Laravel applications. Clients request exactly what they need, reducing over-fetching and improving performance.
Building GraphQL APIs? Contact ZIRA Software for development assistance.