Add as Composer dependency:
composer require simpod/graphql-utils
Instead of defining your schema as an array, use can use more objective-oriented approach. This library provides set of strictly typed builders that help you build your schema.
✔️ Standard way with webonyx/graphql-php
<?php
use GraphQL\Type\Definition\ObjectType;
use GraphQL\Type\Definition\ResolveInfo;
$userType = new ObjectType([
'name' => 'User',
'description' => 'Our blog visitor',
'fields' => [
'firstName' => [
'type' => Type::string(),
'description' => 'User first name'
],
'email' => Type::string()
],
'resolveField' => static function(User $user, $args, $context, ResolveInfo $info) {
switch ($info->fieldName) {
case 'name':
return $user->getName();
case 'email':
return $user->getEmail();
default:
return null;
}
}
]);
✨ The same can be produced in objective way
<?php
use GraphQL\Type\Definition\ObjectType;
use GraphQL\Type\Definition\ResolveInfo;
use SimPod\GraphQLUtils\Builder\ObjectBuilder;
$userType = new ObjectType(
ObjectBuilder::create('User')
->setDescription('Our blog visitor')
->setFields([
FieldBuilder::create('firstName', Type::string())
->setDescription('User first name')
->build(),
FieldBuilder::create('email', Type::string())->build(),
])
->setFieldResolver(
static function(User $user, $args, $context, ResolveInfo $info) {
switch ($info->fieldName) {
case 'name':
return $user->getName();
case 'email':
return $user->getEmail();
default:
return null;
}
}
)
->build()
);
✔️ Standard way with webonyx/graphql-php
<?php
use GraphQL\Type\Definition\EnumType;
$episodeEnum = new EnumType([
'name' => 'Episode',
'description' => 'One of the films in the Star Wars Trilogy',
'values' => [
'NEWHOPE' => [
'value' => 4,
'description' => 'Released in 1977.'
],
'EMPIRE' => [
'value' => 5,
'description' => 'Released in 1980.'
],
'JEDI' => [
'value' => 6,
'description' => 'Released in 1983.'
],
]
]);
✨ The same can be produced in objective way
<?php
use GraphQL\Type\Definition\EnumType;
use SimPod\GraphQLUtils\Builder\EnumBuilder;
$episodeEnum = new EnumType(
EnumBuilder::create('Episode')
->setDescription('One of the films in the Star Wars Trilogy')
->addValue(4, 'NEWHOPE', 'Released in 1977.')
->addValue(5, 'EMPIRE', 'Released in 1980.')
->addValue(6, 'JEDI', 'Released in 1983.')
->build()
);
✔️ Standard way with webonyx/graphql-php
<?php
use GraphQL\Type\Definition\InterfaceType;
use GraphQL\Type\Definition\Type;
$character = new InterfaceType([
'name' => 'Character',
'description' => 'A character in the Star Wars Trilogy',
'fields' => [
'id' => [
'type' => Type::nonNull(Type::string()),
'description' => 'The id of the character.',
],
'name' => [
'type' => Type::string(),
'description' => 'The name of the character.'
]
],
'resolveType' => static function ($value) : object {
if ($value->type === 'human') {
return MyTypes::human();
}
return MyTypes::droid();
}
]);
✨ The same can be produced in objective way
<?php
use GraphQL\Type\Definition\InterfaceType;
use GraphQL\Type\Definition\Type;
use SimPod\GraphQLUtils\Builder\InterfaceBuilder;
use SimPod\GraphQLUtils\Builder\FieldBuilder;
$character = new InterfaceType(
InterfaceBuilder::create('Character')
->setDescription('A character in the Star Wars Trilogy')
->setFields([
FieldBuilder::create('id', Type::nonNull(Type::string()))
->setDescription('The id of the character.')
->build(),
FieldBuilder::create('name', Type::string())
->setDescription('The name of the character.')
->build()
])
->setResolveType(
static function ($value) : object {
if ($value->type === 'human') {
return MyTypes::human();
}
return MyTypes::droid();
}
)
->build()
);
✔️ Standard way with webonyx/graphql-php
<?php
use GraphQL\Type\Definition\UnionType;
$searchResultType = new UnionType([
'name' => 'SearchResult',
'types' => [
MyTypes::story(),
MyTypes::user()
],
'resolveType' => static function($value) {
if ($value->type === 'story') {
return MyTypes::story();
}
return MyTypes::user();
}
]);
✨ The same can be produced in objective way
<?php
use SimPod\GraphQLUtils\Builder\UnionBuilder;
$character = new UnionType(
UnionBuilder::create('SearchResult')
->setTypes([
MyTypes::story(),
MyTypes::user()
])
->setResolveType(
static function($value) {
if ($value->type === 'story') {
return MyTypes::story();
}
return MyTypes::user();
}
)
->build()
);
scalar type that produces scalar DateTime
in your schema.
SimPod\GraphQLUtils\Type\DateTimeType
Extending your exception with SimPod\GraphQLUtils\Error\Error
forces you to implement getType()
method.
Example Error class
<?php
use SimPod\GraphQLUtils\Error\Error;
final class InvalidCustomerIdProvided extends Error
{
private const TYPE = 'INVALID_CUSTOMER_ID_PROVIDED';
public static function noneGiven() : self
{
return new self('No CustomerId provided');
}
public function getType() : string
{
return self::TYPE;
}
public function isClientSafe() : bool
{
return true;
}
}
Create your formatter
<?php
use GraphQL\Error\Error;
use SimPod\GraphQLUtils\Error\FormattedError;
$formatError = static function (Error $error) : array
{
if (! $error->isClientSafe()) {
// eg. log error
}
return FormattedError::createFromException($error);
};
$errorFormatterCallback = static function (Error $error) use ($formatError) : array {
return $formatError($error);
};
$config = GraphQL::executeQuery(/* $args */)
->setErrorFormatter($errorFormatterCallback)
->setErrorsHandler(
static function (array $errors, callable $formatter) : array {
return array_map($formatter, $errors);
}
);
Error types will then be provided in your response so client can easier identify the error type
{
"errors": [
{
"message": "No CustomerId provided",
"extensions": {
"type": "INVALID_CUSTOMER_ID_PROVIDED",
"category": "graphql"
}
}
]
}