Install the Bundle
composer require overblog/graphql-bundle
Add to config/bundles.php:
return [
// ...
Overblog\GraphQLBundle\OverblogGraphQLBundle::class => ['all' => true],
];
Configure Basic Schema
Create a schema configuration file (e.g., config/graphql/schema.yaml):
schema:
query: App\\GraphQL\\Query
mutation: App\\GraphQL\\Mutation
subscription: App\\GraphQL\\Subscription
Define Your First Query
Create a Query class in src/GraphQL/Query.php:
namespace App\GraphQL;
use GraphQL\Type\Definition\Type;
use Overblog\GraphQLBundle\Definition\Resolver\ResolverInterface;
class Query
{
public function hello()
{
return 'world';
}
}
Test the Endpoint
Visit /graphql in your browser or use a client like Postman/GraphQL Playground with the query:
query {
hello
}
Define a Type
Create a UserType in src/GraphQL/Type/UserType.php:
namespace App\GraphQL\Type;
use App\Entity\User;
use GraphQL\Type\Definition\Type;
use Overblog\GraphQLBundle\Definition\Type\ObjectType;
class UserType extends ObjectType
{
protected function buildFields(): array
{
return [
'id' => Type::id(),
'name' => Type::string(),
];
}
public function getName(): string
{
return 'User';
}
public function getEntityClass(): string
{
return User::class;
}
}
Resolve the Type in Query
Update Query.php:
use App\GraphQL\Type\UserType;
use GraphQL\Type\Definition\Type;
class Query
{
public function user(UserType $type, int $id)
{
return $this->getDoctrine()->getRepository(User::class)->find($id);
}
}
Query in GraphQL Playground
query {
user(id: 1) {
id
name
}
}
ObjectType, InputType, and InterfaceType to model your domain.// src/GraphQL/Type/PostType.php
class PostType extends ObjectType
{
protected function buildFields(): array
{
return [
'title' => Type::string(),
'author' => new UserType(),
'comments' => Type::listOf(new CommentType()),
];
}
}
# config/packages/overblog_graphql.yaml
overblog_graphql:
resolvers_cache: true
Pattern: Use InputType for mutations to validate input data.
Example:
// src/GraphQL/Type/CreateUserInputType.php
class CreateUserInputType extends InputType
{
protected function buildFields(): array
{
return [
'name' => Type::nonNull(Type::string()),
'email' => Type::nonNull(Type::string()),
];
}
}
Mutation:
// src/GraphQL/Mutation.php
class Mutation
{
public function createUser(CreateUserInputType $input)
{
$user = new User();
$user->setName($input->getName());
$user->setEmail($input->getEmail());
$this->getDoctrine()->getManager()->persist($user);
$this->getDoctrine()->getManager()->flush();
return $user;
}
}
GlobalIdType and NodeInterface for Relay-compatible nodes.// src/GraphQL/Type/UserType.php
use Overblog\GraphQLBundle\Definition\Type\NodeInterface;
class UserType extends ObjectType implements NodeInterface
{
public function getGlobalIdField(): string
{
return 'id';
}
}
doctrine:generate:types command to scaffold types from Doctrine entities.
php bin/console overblog:graphql:doctrine:generate:types
# config/routes.yaml
graphql:
path: /graphql
methods: [GET, POST]
defaults:
_controller: overblog_graphql.controller.graphql_action
_format: json
requirements:
_method: GET|POST
role: ROLE_USER
# config/packages/overblog_graphql.yaml
overblog_graphql:
subscriptions:
messenger: true
Circular References
User has posts, Post has author).@deprecated or lazy-loading with resolveValue():
protected function resolveValue($value)
{
if ($value && $value->getAuthor()) {
return $value;
}
return null;
}
Resolver Context
ResolverInterface:
use Overblog\GraphQLBundle\Definition\Resolver\ResolverInterface;
class Query implements ResolverInterface
{
private $doctrine;
public function __construct(Doctrine $doctrine)
{
$this->doctrine = $doctrine;
}
public function user($root, array $args)
{
return $this->doctrine->getRepository(User::class)->find($args['id']);
}
}
Batch Loading
DataLoader for batch loading:
use Overblog\GraphQLBundle\DataLoader\DataLoader;
class Query
{
private $dataLoader;
public function __construct(DataLoader $dataLoader)
{
$this->dataLoader = $dataLoader;
}
public function user($root, array $args)
{
return $this->dataLoader->loadMany(User::class, [$args['id']]);
}
}
Enable Debug Mode
# config/packages/overblog_graphql.yaml
overblog_graphql:
debug: true
GraphQL Playground
/graphql for interactive debugging.Logging
overblog_graphql:
logging: true
Schema Stitching
Schema::merge():
use GraphQL\Type\Schema;
$schema = Schema::merge([
new Schema([$queryType, $mutationType]),
new Schema([$otherQueryType]),
]);
Custom Directives
@auth) for fine-grained access control:
use Overblog\GraphQLBundle\Definition\Directive\Directive;
class AuthDirective extends Directive
{
protected function configure()
{
$this->type('auth');
}
}
Performance Optimization
resolveType() for interfaces/unions to optimize queries.ConnectionType:
use Overblog\GraphQLBundle\Definition\Type\ConnectionType;
class Query
{
public function posts(ConnectionType $connection)
{
return $connection->getConnection($this->getDoctrine()->getRepository(Post::class));
}
How can I help you explore Laravel packages today?