mirror of
https://github.com/retailcrm/graphql-php.git
synced 2025-02-06 07:49:24 +03:00
Documented lazy loading of types in schema and ability to define schema using GraphQL type language
This commit is contained in:
parent
e52fe8c384
commit
c65d8d8624
@ -88,3 +88,84 @@ mutation | `ObjectType` | Object type (usually named "Mutation") containing
|
||||
subscription | `ObjectType` | Reserved for future subscriptions implementation. Currently presented for compatibility with introspection query of **graphql-js**, used by various clients (like Relay or GraphiQL)
|
||||
directives | `Directive[]` | Full list of [directives](directives/) supported by your schema. By default contains built-in `@skip` and `@include` directives.<br><br> If you pass your own directives and still want to use built-in directives - add them explicitly. For example: `array_merge(GraphQL::getInternalDirectives(), [$myCustomDirective]`
|
||||
types | `ObjectType[]` | List of object types which cannot be detected by **graphql-php** during static schema analysis.<br><br>Most often it happens when object type is never referenced in fields directly, but is still a part of schema because it implements an interface which resolves to this object type in it's `resolveType` callback. <br><br> Note that you are not required to pass all of your types here - it is simply a workaround for concrete use-case.
|
||||
|
||||
# Lazy loading of types
|
||||
By default a schema will scan all of your type and field definitions to serve GraphQL queries.
|
||||
It may cause performance overhead when there are many types in a schema.
|
||||
|
||||
In this case it is recommended to pass **typeLoader** option to schema constructor and define all
|
||||
of your object **fields** as callbacks.
|
||||
|
||||
Type loading concept is very similar to PHP class loading, but keep in mind that **typeLoader** must
|
||||
always return the same instance of a type.
|
||||
|
||||
Usage example:
|
||||
```php
|
||||
class Types
|
||||
{
|
||||
private $registry = [];
|
||||
|
||||
public function get($name)
|
||||
{
|
||||
if (!isset($this->types[$name])) {
|
||||
$this->types[$name] = $this->{$name}();
|
||||
}
|
||||
return $this->types[$name];
|
||||
}
|
||||
|
||||
private function MyTypeA()
|
||||
{
|
||||
return new ObjectType([
|
||||
'name' => 'MyTypeA',
|
||||
'fields' => function() {
|
||||
return [
|
||||
'b' => ['type' => $this->get('MyTypeB')]
|
||||
];
|
||||
}
|
||||
]);
|
||||
}
|
||||
|
||||
private function MyTypeB()
|
||||
{
|
||||
// ...
|
||||
}
|
||||
}
|
||||
|
||||
$registry = new Types();
|
||||
|
||||
$schema = new Schema([
|
||||
'query' => $registry->get('Query'),
|
||||
'typeLoader' => function($name) use ($registry) {
|
||||
return $registry->get($name);
|
||||
}
|
||||
]);
|
||||
```
|
||||
|
||||
|
||||
# Schema Validation
|
||||
By default schema is created with only shallow validation of type and field definitions
|
||||
(because validation requires full schema scan and is very costly on bigger schemas).
|
||||
|
||||
But there is a special method `$schema->assertValid()` which throws `GraphQL\Error\InvariantViolation`
|
||||
exception when it encounters any error, like:
|
||||
|
||||
- Invalid types used for fields / arguments
|
||||
- Missing interface implementations
|
||||
- Invalid interface implementations
|
||||
- Other schema errors...
|
||||
|
||||
Schema validation is supposed to be used in CLI commands or during build step of your app.
|
||||
Don't call it in web requests in production.
|
||||
|
||||
Usage example:
|
||||
```php
|
||||
$schema = new GraphQL\Type\Schema([
|
||||
'query' => $myQueryType
|
||||
]);
|
||||
|
||||
try {
|
||||
$schema->assertValid();
|
||||
} catch (GraphQL\Error\InvariantViolation $e) {
|
||||
echo $e->getMessage();
|
||||
}
|
||||
```
|
||||
|
86
docs/type-system/type-language.md
Normal file
86
docs/type-system/type-language.md
Normal file
@ -0,0 +1,86 @@
|
||||
# Defining your schema
|
||||
|
||||
[Type language](http://graphql.org/learn/schema/#type-language) is a convenient way to define your schema,
|
||||
especially with IDE autocompletion and syntax validation.
|
||||
|
||||
Here is a simple schema defined in GraphQL type language (e.g. in separate **schema.graphql** file):
|
||||
|
||||
```graphql
|
||||
schema {
|
||||
query: Query
|
||||
mutation: Mutation
|
||||
}
|
||||
|
||||
type Query {
|
||||
greetings(input: HelloInput!): String!
|
||||
}
|
||||
|
||||
input HelloInput {
|
||||
firstName: String!
|
||||
lastName: String
|
||||
}
|
||||
```
|
||||
|
||||
In order to create schema instance out of this file, use `GraphQL\Utils\BuildSchema`:
|
||||
|
||||
```php
|
||||
<?php
|
||||
use GraphQL\Utils\BuildSchema;
|
||||
|
||||
$contents = file_get_contents('schema.graphql');
|
||||
$schema = BuildSchema::build($contents);
|
||||
```
|
||||
|
||||
By default such schema is created without any resolvers. As a result it doesn't support **Interfaces** and **Unions**
|
||||
because it is impossible to resolve actual implementations during execution.
|
||||
|
||||
Also we have to rely on [default field resolver](/data-fetching/#default-field-resolver) and **root value** in
|
||||
order to execute query against this schema.
|
||||
|
||||
# Defining resolvers
|
||||
In order to enable **Interfaces**, **Unions** and custom field resolvers you can pass second argument:
|
||||
**type config decorator** to schema builder.
|
||||
|
||||
It accepts default type config produced by builder and is expected to add missing options like
|
||||
[`resolveType`](/type-system/interfaces/#configuration-options) for interface types or
|
||||
[`resolveField`](/type-system/object-types/#configuration-options) for object types.
|
||||
|
||||
```php
|
||||
<?php
|
||||
use GraphQL\Utils\BuildSchema;
|
||||
|
||||
$typeConfigDecorator = function($typeConfig, $typeDefinitionNode) {
|
||||
$name = $typeConfig['name'];
|
||||
// ... add missing options to $typeConfig based on type $name
|
||||
return $typeConfig;
|
||||
};
|
||||
|
||||
$contents = file_get_contents('schema.graphql');
|
||||
$schema = BuildSchema::build($contents, $typeConfigDecorator);
|
||||
```
|
||||
|
||||
# Performance considerations
|
||||
Method `BuildSchema::build()` produces a [lazy schema](/type-system/schema/#lazy-loading-of-types)
|
||||
automatically, so it works efficiently even with very large schemas.
|
||||
|
||||
But parsing type definition file on each request is suboptimal, so it is recommended to cache
|
||||
intermediate parsed representation of the schema for production environment:
|
||||
|
||||
```php
|
||||
<?php
|
||||
use GraphQL\Language\Parser;
|
||||
use GraphQL\Utils\BuildSchema;
|
||||
use GraphQL\Language\AST\Node;
|
||||
|
||||
$cacheFilename = 'cached_schema.php';
|
||||
|
||||
if (!file_exists($cacheFilename)) {
|
||||
$document = Parser::parse(file_get_contents('./schema.graphql'));
|
||||
file_put_contents($cacheFilename, "<?php\nreturn " . var_export($document->toArray(), true));
|
||||
} else {
|
||||
$document = Node::fromArray(require $cacheFilename); // fromArray() is a lazy operation as well
|
||||
}
|
||||
|
||||
$typeConfigDecorator = function () {};
|
||||
$schema = BuildSchema::build($document, $typeConfigDecorator);
|
||||
```
|
Loading…
x
Reference in New Issue
Block a user