From 24b2285ffed9cf2d962dc461c57d23baaa502fba Mon Sep 17 00:00:00 2001 From: vladar Date: Tue, 8 Nov 2016 17:17:48 +0700 Subject: [PATCH] Work in progress on better docs (added sections on directives and schema to docs) --- docs/type-system/directives.md | 65 +++++++++++++++++++++++++ docs/type-system/index.md | 7 +++ docs/type-system/metadata.md | 0 docs/type-system/schema.md | 88 ++++++++++++++++++++++++++++++++++ mkdocs.yml | 2 +- 5 files changed, 161 insertions(+), 1 deletion(-) create mode 100644 docs/type-system/directives.md delete mode 100644 docs/type-system/metadata.md diff --git a/docs/type-system/directives.md b/docs/type-system/directives.md new file mode 100644 index 0000000..a3ddfe4 --- /dev/null +++ b/docs/type-system/directives.md @@ -0,0 +1,65 @@ +# Built-in directives +Directive is a way to dynamically change the structure and shape of queries using variables. +Directive can be attached to a field or fragment inclusion, and can affect execution of the +query in any way the server desires. + +GraphQL specification includes two built-in directives: + +* `@include(if: Boolean)` Only include this field or fragment in the result if the argument is `true` +* `@skip(if: Boolean)` Skip this field or fragment if the argument is `true` + +For example: +```graphql +query Hero($episode: Episode, $withFriends: Boolean!) { + hero(episode: $episode) { + name + friends @include(if: $withFriends) { + name + } + } +} +``` +Here if `$withFriends` variable is set to `false` - friends section will be ignored and excluded +from response. Important implementation detail: those fields will never be executed +(and not just removed from response after execution). + +# Custom directives +**graphql-php** supports custom directives even though their presence does not affect execution of fields. +But you can use `GraphQL\Type\Definition\ResolveInfo` in field resolvers to modify the output depending +on those directives or perform statistics collection. + +Other use case is your own query validation rules relying on custom directives. + +In **graphql-php** custom directive is an instance of `GraphQL\Type\Definition\Directive` +(or one of it subclasses) which accepts an array with following options: + +```php +use GraphQL\Type\Definition\Directive; +use GraphQL\Type\Definition\FieldArgument; + +$trackDirective = new Directive([ + 'name' => 'track', + 'description' => 'Instruction to record usage of the field by client' + 'locations' => [ + Directive::LOCATION_FIELD, + ], + 'args' => [ + new FieldArgument([ + 'name' => 'details', + 'type' => Type::string(), + 'description' => 'String with additional details of field usage scenario' + 'defaultValue' => '' + ]) + ] +]); +``` + +Directive location can be one of the following values: + +* `Directive::LOCATION_QUERY` +* `Directive::LOCATION_MUTATION` +* `Directive::LOCATION_SUBSCRIPTION` +* `Directive::LOCATION_FIELD` +* `Directive::LOCATION_FRAGMENT_DEFINITION` +* `Directive::LOCATION_FRAGMENT_SPREAD` +* `Directive::LOCATION_INLINE_FRAGMENT` diff --git a/docs/type-system/index.md b/docs/type-system/index.md index ed36889..7ea1a17 100644 --- a/docs/type-system/index.md +++ b/docs/type-system/index.md @@ -156,3 +156,10 @@ introduce Dependency Injection Container if your types have other dependencies. Alternatively all methods of registry could be static if you prefer - then there is no need to pass it in constructor - instead just use use `TypeRegistry::myAType()` in your type definitions. + +# Custom Metadata +All types in **graphql-php** accept configuration array. In some cases you may be interested in +passing your own metadata for type or field definition. + +**graphql-php** preserves original configuration array in every type or field instance in +public property `$config`. Use it to implement app-level mappings and definitions. diff --git a/docs/type-system/metadata.md b/docs/type-system/metadata.md deleted file mode 100644 index e69de29..0000000 diff --git a/docs/type-system/schema.md b/docs/type-system/schema.md index e69de29..14a701d 100644 --- a/docs/type-system/schema.md +++ b/docs/type-system/schema.md @@ -0,0 +1,88 @@ +# Schema Definition + +After all of your types are defined, you must define schema. Schema is a container for your type +hierarchy, which expects root type in constructor. + +In **graphql-php** schema is an instance of `GraphQL\Schema` which accepts configuration array +in constructor: + +```php +$schema = new Schema([ + 'query' => $queryType, + 'mutation' => $mutationType, +]); +``` + +# Configuration Options + +Option | Type | Notes +------------ | -------- | ----- +query | `ObjectType` | **Required.** Object type (usually named "Query") containing root-level fields of your read API +mutation | `ObjectType` | Object type (usually named "Mutation") containing root-level fields of your write API +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.

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.

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.

Note that you are not required to pass all of your types here - it is simply a workaround for concrete use-case. + + +# Query and Mutation types +Schema consists of two special root types: + +* `Query` type is a surface of your read API +* `Mutation` type (optional) exposes write API by declaring all possible mutations in your app. + +Query and Mutation types are regular object types containing root-level fields of your API: + +```php +use GraphQL\Type\Definition\ObjectType; +use GraphQL\Type\Definition\Type; +use GraphQL\Schema; + +$queryType = new ObjectType([ + 'name' => 'Query', + 'fields' => [ + 'hello' => [ + 'type' => Type::string(), + 'resolve' => function() { + return 'Hello World!'; + } + ] + 'hero' => [ + 'type' => $characterInterface, + 'args' => [ + 'episode' => [ + 'type' => $episodeEnum + ] + ], + 'resolve' => function ($rootValue, $args) { + return StarWarsData::getHero(isset($args['episode']) ? $args['episode'] : null); + }, + ] + ] +]); + +$mutationType = new ObjectType([ + 'name' => 'Mutation', + 'fields' => [ + 'createReviewForEpisode' => [ + 'type' => $createReviewForEpisode, + 'args' => [ + 'episode' => $episodeEnum, + 'review' => $reviewInputObject + ], + 'resolve' => function() { + + } + ] + ] +]); +``` + +Keep in mind that other than the special meaning of declaring surface area of your API, +those types are the same as any other object type, and their fields work exactly the same way. + +Resolvers of those fields receive `$rootValue` which you pass into execute call: +`GraphQL::execute($schema, $query, $rootValue)` + +`Mutation` type is also just a regular object type. The difference is in semantics. +Field names of Mutation type are usually verbs and they almost always have arguments - quite often +with complex input values (see [Input Types](input-types/) for details). diff --git a/mkdocs.yml b/mkdocs.yml index 1f6bfee..67df152 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -11,8 +11,8 @@ pages: - Interfaces: type-system/interfaces.md - Unions: type-system/unions.md - Input Types: type-system/input-types.md + - Directives: type-system/directives.md - Defining Schema: type-system/schema.md - - Custom Metadata: type-system/metadata.md - Data Fetching: data-fetching.md - Best Practices: best-practices.md - Complementary Tools: complementary-tools.md