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)
|
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]`
|
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.
|
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