besmartand-pro/graphqlite-bundle
## Getting Started
### Minimal Setup
1. **Install the Bundle**
```bash
composer require besmartand-pro/graphqlite-bundle
Ensure BeSmartAnd\Pro\GraphQLiteBundle\GraphQLiteBundle::class is registered in config/bundles.php.
Configure the Bundle
Add a minimal graphqlite.yaml config under config/packages/besmartand_pro_graphqlite.yaml:
graphqlite:
schema:
query: App\\GraphQL\\Query
mutation: App\\GraphQL\\Mutation
Define a Query Class
Create a basic query resolver in src/GraphQL/Query.php:
namespace App\GraphQL;
use GraphQLite\Query\Query;
use GraphQLite\Query\Type\Type;
class Query extends Query
{
public function hello(): string
{
return 'world';
}
}
Test the Endpoint
Access /graphql with a query:
query {
hello
}
Use GraphQLite to expose a simple API endpoint for a Doctrine entity (e.g., User):
// src/GraphQL/Query.php
public function users(): array
{
return $this->getDoctrine()->getRepository(User::class)->findAll();
}
Query:
query {
users {
id
name
}
}
Modular Queries/Mutations
Split logic into separate classes (e.g., UserQuery, PostMutation) and reference them in config:
graphqlite:
schema:
query: ['App\GraphQL\UserQuery', 'App\GraphQL\PostQuery']
Type System
Define custom types (e.g., UserType) for complex responses:
use GraphQLite\Query\Type\Type;
class UserType extends Type
{
public function getType(): string
{
return 'User';
}
public function getFields(): array
{
return [
'id' => fn(User $user) => $user->getId(),
'email' => fn(User $user) => $user->getEmail(),
];
}
}
Dependency Injection
Inject services (e.g., UserRepository) into resolvers:
public function __construct(private UserRepository $userRepository) {}
public function user(int $id): ?User
{
return $this->userRepository->find($id);
}
Authentication Use Symfony’s security component to protect queries:
use Symfony\Component\Security\Core\Security;
public function __construct(private Security $security) {}
public function me(): ?User
{
return $this->security->getUser();
}
Validation Validate input arguments with Symfony’s Validator:
use Symfony\Component\Validator\Validator\ValidatorInterface;
public function createUser(string $name, string $email, ValidatorInterface $validator)
{
$user = new User($name, $email);
$errors = $validator->validate($user);
if (count($errors)) {
throw new \RuntimeException('Validation failed');
}
// Save user...
}
DataLoader for N+1 Queries
Use GraphQLite\DataLoader\DataLoader to batch-load related entities:
public function posts(): array
{
$posts = $this->postRepository->findAll();
$userIds = array_map(fn($post) => $post->getUserId(), $posts);
$users = $this->dataLoader->loadMany($userIds, User::class);
return array_map(fn($post) => [
'post' => $post,
'author' => $users[$post->getUserId()],
], $posts);
}
Caching Cache query results with Symfony’s cache system:
use Symfony\Contracts\Cache\CacheInterface;
public function __construct(private CacheInterface $cache) {}
public function trendingPosts(): array
{
return $this->cache->get('trending_posts', function() {
return $this->postRepository->findTrending();
}, null, ['graphql']);
}
Schema Registration
graphqlite.yaml.php bin/console cache:clear).Circular Dependencies
User resolver calling Post resolver which calls User).DataLoader or flatten the response structure.Type Mismatches
null when a non-nullable field is expected).@nonnull in GraphQL schema or handle null cases explicitly.Doctrine Lazy Loading
LazyLoadingException.->getId() or ->getOneOrNewReference() to trigger loading, or fetch eager data.Enable GraphQL Playground
Install the graphql-playground package and configure it in config/packages/graphqlite.yaml:
graphqlite:
playground: true
Access /graphql for an interactive IDE.
Logging Queries
Enable debug logging in config/packages/monolog.yaml:
handlers:
graphql:
type: stream
path: "%kernel.logs_dir%/graphql.log"
level: debug
channels: ["graphql"]
Validation Errors
errors field in the response for validation failures.GraphQLite\Query\Exception\ValidationException to throw custom errors:
throw new ValidationException('Invalid input', ['field' => 'Invalid value']);
Custom Directives
Extend GraphQLite with custom directives (e.g., @auth):
use GraphQLite\Query\Directive\Directive;
class AuthDirective extends Directive
{
public function getName(): string { return 'auth'; }
public function getLocations(): array { return [self::QUERY, self::MUTATION]; }
}
Middleware Add middleware to modify requests/responses:
use GraphQLite\Query\Middleware\Middleware;
class LoggingMiddleware implements Middleware
{
public function handle(Request $request, callable $next): Response
{
\Log::info('GraphQL request', ['query' => $request->getQuery()]);
return $next($request);
}
}
Register in config:
graphqlite:
middleware: ['App\GraphQL\LoggingMiddleware']
Subscriptions
Use GraphQLite\Query\Subscription\Subscription for real-time updates (requires symfony/ux-live-component or similar):
class Subscription extends \GraphQLite\Query\Subscription\Subscription
{
public function newPost(): \Generator
{
yield from $this->postRepository->getNewPostStream();
}
}
Autowiring Resolvers Ensure resolvers are autowireable by:
Schema Caching Disable caching during development:
graphqlite:
schema:
cache: false
Custom Scalar Types
Register custom scalars (e.g., DateTime) in config:
graphqlite:
scalars:
DateTime: App\GraphQL\Scalar\DateTimeScalar
---
How can I help you explore Laravel packages today?