webonyx/graphql-php
PHP implementation of the GraphQL specification, based on graphql-js. Build schemas, types, and execute queries/mutations in your PHP apps. Widely used, well-tested, and documented with examples and class reference.
Install the package:
composer require webonyx/graphql-php
Define a basic schema (e.g., schema.php):
use GraphQL\Type\Definition\Type;
use GraphQL\Type\Definition\ObjectType;
use GraphQL\GraphQL;
$queryType = new ObjectType([
'name' => 'Query',
'fields' => [
'hello' => [
'type' => Type::string(),
'resolve' => fn() => 'World',
],
],
]);
$schema = new \GraphQL\Schema([
'query' => $queryType,
]);
$query = '{ hello }';
$result = GraphQL::executeQuery($schema, $query);
Test with a simple query (e.g., via php artisan or a CLI script):
echo $result->toString();
GraphQL::executeQuery() method to run queries against your schema.$query = '
query {
user(id: 1) {
name
email
}
}
';
$result = GraphQL::executeQuery($schema, $query);
Modular Schema Construction:
UserType, PostType) separately and compose them into the schema.$userType = new ObjectType([
'name' => 'User',
'fields' => [
'id' => ['type' => Type::int()],
'name' => ['type' => Type::string()],
],
]);
$schema = new \GraphQL\Schema([
'query' => new ObjectType([
'name' => 'Query',
'fields' => [
'user' => [
'type' => $userType,
'args' => ['id' => ['type' => Type::int()]],
'resolve' => fn($root, $args) => User::find($args['id']),
],
],
]),
]);
Reusing Types:
Type::listOf(), Type::nonNull(), and interfaces to avoid duplication.$commentType = new ObjectType([
'name' => 'Comment',
'fields' => [
'text' => ['type' => Type::string()],
'author' => ['type' => $userType],
],
]);
Data Fetching:
resolve functions to fetch data from databases, APIs, or other sources.'resolve' => fn($root, $args) => Post::with('author')->find($args['id']),
Context Injection:
GraphQL::executeQuery():
$context = ['user' => auth()->user()];
$result = GraphQL::executeQuery($schema, $query, null, null, $context);
Optimized Resolvers:
ResolveInfo to optimize queries (e.g., select only requested fields):
'resolve' => fn($post, $args, $context, $info) => {
$fields = $info->fieldNodes[0]->selectionSet->selections;
return Post::select(array_map(fn($f) => $f->name->value, $fields))->find($post->id);
},
Middleware for GraphQL:
Route::middleware(['graphql.auth'])->post('/graphql', function () {
return response()->json(GraphQL::executeQuery($schema, $request->input('query')));
});
Service Providers:
AppServiceProvider:
public function boot()
{
$this->app->singleton(\GraphQL\Schema::class, fn() => $this->app->make(\GraphQL\Schema::class));
}
Laravel Scout Integration:
'resolve' => fn($root, $args) => Post::search($args['query'])->get(),
Circular References:
User resolving to Post which resolves back to User).Type::nonNull() or lazy-loading to mitigate.N+1 Queries:
'resolve' => fn($root, $args) => Post::with('author', 'comments')->find($args['id']),
Type Mismatches:
int for Type::int()).Performance:
QueryComplexity or QueryDepth to limit them:
DocumentValidator::addRule(new QueryComplexity(1000));
Enable Error Reporting:
GraphQL::executeQuery() with debugFlag:
$result = GraphQL::executeQuery($schema, $query, null, null, null, null, ['debugFlag' => true]);
Introspection:
__schema query to debug types:
query {
__schema {
types {
name
fields {
name
type {
name
}
}
}
}
}
Logging:
'resolve' => fn($post) => {
Log::debug('Resolving post', ['post' => $post->toArray()]);
return $post;
},
Disable Introspection:
DocumentValidator::addRule(new DisableIntrospection());
Custom Scalar Types:
$schema->addCustomScalarType(new DateTimeType());
Validation Rules:
DocumentValidator::addRule(new class implements Rule {
public function validate($document) {
if (!auth()->check()) {
throw new Error('Unauthorized');
}
}
});
Custom Directives:
@auth):
$schema->addDirective(new AuthDirective());
Middleware:
$schema->addMiddleware(new class implements Middleware {
public function resolve($next) {
return $next();
}
});
Error Handling:
$result = GraphQL::executeQuery($schema, $query, null, null, null, null, ['errorFormatter' => fn($e) => ['message' => $e->getMessage()]]);
Route Caching:
Route::cache(['graphql']);
Artisan Commands:
php artisan make:graphql-type User
Testing:
GraphQLTestCase for testing:
public function testQuery()
{
$query = '{ user(id: 1) { name } }';
$result = $this->graphQL($query);
$result->assertNoErrors()->assertJson(['data' => ['user' => ['name' => 'John']]]);
}
How can I help you explore Laravel packages today?