This example serves as a simple tutorial on how to create a simple type and what must be done for request execution.
In this example, we define our simple schema and execute one request on it - all in five simple steps.
Schema is mainly defined by types. Each type is a separate class extending one of the abstract classes.
\Graphpinator\Typesystem\Type\Graphpinator\Typesystem\UnionType\Graphpinator\Typesystem\InterfaceType\Graphpinator\Typesystem\ScalarType\Graphpinator\Typesystem\EnumType\Graphpinator\Typesystem\InputTypeIn this example, we only define one type - the Query type.
<?php
declare(strict_types = 1);
namespace Example;
use Graphpinator\Typesystem\Attribute\Description;
use Graphpinator\Typesystem\Container;
use Graphpinator\Typesystem\Field\ResolvableField;
use Graphpinator\Typesystem\Field\ResolvableFieldSet;
use Graphpinator\Typesystem\Type;
#[Description('Graphpinator HelloWorld: Query type')]
final class Query extends Type
{
protected const NAME = 'Query';
protected const DESCRIPTION = 'Graphpinator HelloWorld: Query type';
public function validateNonNullValue($rawValue) : bool
{
// validation of resolved value from parent, Query is the initial type in the schema = has no parent
return true;
}
protected function getFieldDefinition() : ResolvableFieldSet
{
// types return ResolvableFieldSet, which is a map of ResolvableField (Fields with resolve function)
// interface only define FieldSet, which is a map of Field, which does not have resolve function but only define the signature
return new ResolvableFieldSet([
new ResolvableField(
'helloWorld',
Container::String()->notNull(),
function ($parent) : string {
return 'Hello world!';
},
),
]);
}
}
All available types for our GraphQL service are stored in a \Graphpinator\Typesystem\Container class.
In this example we are going to assign the type to the
Containermanually, but this step can (and is recommended to) be achieved automatically when using some dependency injection.
$query = new \Example\Query(); // our Query class which we defined above
$container = new \Graphpinator\SimpleContainer([$query], []);
\Graphpinator\Typesystem\Schema is a class that contains all information about your service.
Schema needs Container of its types, and also information which types manage different operation types (query, mutation, subscription).
In the following code snippet, we assign our $container to it and also declare that the type to manage query operation is our Query type.
In more complex services, where we would use mutation or subscription operation types, we would also pass the third and fourth parameter.
This step is also skipped when using some dependency injection solution.
$schema = new \Graphpinator\Typesystem\Schema($container, $query);
This step requires
infinityloop-dev/graphpinator-printerpackage.
We can use our Schema class to print its definition in the type language syntax, which describes the capabilities of our GraphQL service.
$printer = new \Graphpinator\Printer\Printer();
echo $printer->printSchema($schema);
produces the following
schema {
query: Query
mutation: null
subscription: null
}
"""
Graphpinator HelloWorld: Query type
"""
type Query {
helloWorld: String!
}
\Graphpinator\Graphpinator is a class that brings everything together and is the main class you would be using.
It contains your Schema, logger, and other information needed to execute a request against your service.
$graphpinator = new \Graphpinator\Graphpinator(
$schema,
true, // optional bool - whether to catch exceptions (Dont worry! Only graphpinator errors are printed in response, eg. syntax errors)
$modules, // optional \Graphpinator\Module\ModuleSet - extending functionality using modules
$loggerInterface // optional \Psr\Log\LoggerInterface - logging queries and errors
);
Final step! The last thing we need to do is to create an appropriate \Graphpinator\Request\RequestFactory.
Classes implementing RequestFactory are responsible for creation of an Request object,
which is basicaly an extraction of query, variables and operationName from various sources.
GraPHPinator has multiple RequestFactory implementations ready, from which you may choose.
All depends on how your low-level request looks like and what middleware you use.
\Graphpinator\Request\JsonRequestFactory
query, variables and operationName.\Graphpinator\Utils\Json.\Graphpinator\Request\PsrRequestFactory
\Psr\Http\Message\ServerRequestInterface.In this simple example, we choose the JsonRequestFactory.
$json = \Infinityloop\Utils\Json::fromString(
'{"query":"query { helloWorld }"}'
);
$requestFactory = new \Graphpinator\Request\JsonRequestFactory($json);
$response = $graphpinator->run($requestFactory);
This is it, we have our response in $response variable.
It is an object of class \Graphpinator\Response (which is \JsonSerializable) containing resolved data and errors.
echo $response->toString();
produces the following
{"data":{"helloWorld": "Hello world!"}}
This is the end of the Hello world example, thank you for reading this far.
Example configuration for Nette framework, which automatically
SimpleContainer and assigns all found typesSchemaservices:
- Graphpinator\SimpleContainer
- Graphpinator\Typesystem\Schema(
@Graphpinator\SimpleContainer,
@Example\Query,
null, # Mutation
null # Subscription
)
search:
graphql:
in: '%appDir%/GraphQL'
extends:
- Graphpinator\Typesystem\Contract\NamedType
Similar approach is surely available for other frameworks as well.
If you are using some other framework and wish to expand this example, please send a PR.