Skip to content

Commit 7f346d5

Browse files
committed
Documentation improvements
1 parent de79153 commit 7f346d5

22 files changed

+710
-376
lines changed

UPGRADE.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,8 @@ It requires promise adapter in it's first argument and always returns a `Promise
146146
Old methods `GraphQL::execute` and `GraphQL::executeAndReturnResult` still work in backwards-compatible manner,
147147
but they are deprecated and will be removed eventually.
148148

149+
Same applies to Executor: use `Executor::promiseToExecute()` vs `Executor::execute()`.
150+
149151
## Upgrade v0.7.x > v0.8.x
150152
All of those changes apply to those who extends various parts of this library.
151153
If you only use the library and don't try to extend it - everything should work without breaks.

docs/complementary-tools.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,4 @@
1111
- [ChromeiQL](https://chrome.google.com/webstore/detail/chromeiql/fkkiamalmpiidkljmicmjfbieiclmeij)
1212
or [GraphiQL Feen](https://chrome.google.com/webstore/detail/graphiql-feen/mcbfdonlkfpbfdpimkjilhdneikhfklp) -
1313
GraphiQL as Google Chrome extension
14+
- [DataLoader PHP](https://github.com/overblog/dataloader-php) - as a ready implementation for [deferred resolvers](data-fetching.md#solving-n1-problem)

docs/data-fetching.md

Lines changed: 40 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@
22
GraphQL is data-storage agnostic. You can use any underlying data storage engine, including SQL or NoSQL database,
33
plain files or in-memory data structures.
44

5-
In order to convert GraphQL query to PHP array **graphql-php** traverses query fields (using depth-first algorithm) and
5+
In order to convert the GraphQL query to PHP array, **graphql-php** traverses query fields (using depth-first algorithm) and
66
runs special **resolve** function on each field. This **resolve** function is provided by you as a part of
77
[field definition](type-system/object-types.md#field-configuration-options) or [query execution call](executing-queries.md#overview).
88

9-
Result returned by **resolve** function is directly included in response (for scalars and enums)
9+
Result returned by **resolve** function is directly included in the response (for scalars and enums)
1010
or passed down to nested fields (for objects).
1111

1212
Let's walk through an example. Consider following GraphQL query:
@@ -25,6 +25,9 @@ Let's walk through an example. Consider following GraphQL query:
2525
We need a Schema that can fulfill it. On the very top level the Schema contains Query type:
2626

2727
```php
28+
<?php
29+
use GraphQL\Type\Definition\ObjectType;
30+
2831
$queryType = new ObjectType([
2932
'name' => 'Query',
3033
'fields' => [
@@ -46,12 +49,16 @@ $queryType = new ObjectType([
4649

4750
As we see field **lastStory** has **resolve** function that is responsible for fetching data.
4851

49-
In our example we simply return array value, but in real-world application you would query
50-
your database/cache/search index and return result.
52+
In our example, we simply return array value, but in the real-world application you would query
53+
your database/cache/search index and return the result.
5154

5255
Since **lastStory** is of composite type **BlogStory** this result is passed down to fields of this type:
5356

5457
```php
58+
<?php
59+
use GraphQL\Type\Definition\Type;
60+
use GraphQL\Type\Definition\ObjectType;
61+
5562
$blogStoryType = new ObjectType([
5663
'name' => 'BlogStory',
5764
'fields' => [
@@ -83,8 +90,8 @@ $blogStoryType = new ObjectType([
8390

8491
Here **$blogStory** is the array returned by **lastStory** field above.
8592

86-
Again: in real-world applications you would fetch user data from datastore by **authorId** and return it.
87-
Also note that you don't have to return arrays. You can return any value, **graphql-php** will pass it untouched
93+
Again: in the real-world applications you would fetch user data from data store by **authorId** and return it.
94+
Also, note that you don't have to return arrays. You can return any value, **graphql-php** will pass it untouched
8895
to nested resolvers.
8996

9097
But then the question appears - field **title** has no **resolve** option. How is it resolved?
@@ -95,7 +102,8 @@ for a field you simply override this default resolver.
95102
# Default Field Resolver
96103
**graphql-php** provides following default field resolver:
97104
```php
98-
function defaultFieldResolver($source, $args, $context, ResolveInfo $info)
105+
<?php
106+
function defaultFieldResolver($source, $args, $context, \GraphQL\Type\Definition\ResolveInfo $info)
99107
{
100108
$fieldName = $info->fieldName;
101109
$property = null;
@@ -115,7 +123,7 @@ function defaultFieldResolver($source, $args, $context, ResolveInfo $info)
115123
```
116124

117125
As you see it returns value by key (for arrays) or property (for objects).
118-
If value is not set - it returns **null**.
126+
If the value is not set - it returns **null**.
119127

120128
To override the default resolver, pass it as an argument of [executeQuery](executing-queries.md) call.
121129

@@ -124,6 +132,11 @@ Sometimes it might be convenient to set default field resolver per type. You can
124132
[resolveField option in type config](type-system/object-types.md#configuration-options). For example:
125133

126134
```php
135+
<?php
136+
use GraphQL\Type\Definition\Type;
137+
use GraphQL\Type\Definition\ObjectType;
138+
use GraphQL\Type\Definition\ResolveInfo;
139+
127140
$userType = new ObjectType([
128141
'name' => 'User',
129142
'fields' => [
@@ -167,13 +180,14 @@ Consider following GraphQL query:
167180
}
168181
```
169182

170-
Naive field resolution process would require up to 10 calls to underlying data store to fetch authors for all 10 stories.
183+
Naive field resolution process would require up to 10 calls to the underlying data store to fetch authors for all 10 stories.
171184

172-
**graphql-php** provides tools to mitigate this problem: it allows you to defer actual field resolution to later stage
185+
**graphql-php** provides tools to mitigate this problem: it allows you to defer actual field resolution to a later stage
173186
when one batched query could be executed instead of 10 distinct queries.
174187

175-
Here is an example of `BlogStory` resolver for field `author` that uses deferring:
188+
Here is an example of **BlogStory** resolver for field **author** that uses deferring:
176189
```php
190+
<?php
177191
'resolve' => function($blogStory) {
178192
MyUserBuffer::add($blogStory['authorId']);
179193

@@ -184,18 +198,16 @@ Here is an example of `BlogStory` resolver for field `author` that uses deferrin
184198
}
185199
```
186200

187-
In this example we fill up buffer with 10 author ids first. Then **graphql-php** continues
201+
In this example, we fill up the buffer with 10 author ids first. Then **graphql-php** continues
188202
resolving other non-deferred fields until there are none of them left.
189203

190-
After that it calls `Closures` wrapped by `GraphQL\Deferred` which in turn load all buffered
191-
ids once (using SQL IN(?), Redis MGET or other similar tools) and return final field value.
204+
After that, it calls closures wrapped by `GraphQL\Deferred` which in turn load all buffered
205+
ids once (using SQL IN(?), Redis MGET or other similar tools) and returns final field value.
192206

193207
Originally this approach was advocated by Facebook in their [Dataloader](https://github.com/facebook/dataloader)
194-
project.
195-
196-
This solution enables very interesting optimizations at no cost. Consider following query:
208+
project. This solution enables very interesting optimizations at no cost. Consider the following query:
197209

198-
```
210+
```graphql
199211
{
200212
topStories(limit: 10) {
201213
author {
@@ -212,14 +224,14 @@ This solution enables very interesting optimizations at no cost. Consider follow
212224
}
213225
```
214226

215-
Even if `author` field is located on different levels of query - it can be buffered in the same buffer.
216-
In this example only one query will be executed for all story authors comparing to 20 queries
217-
in naive implementation.
227+
Even though **author** field is located on different levels of the query - it can be buffered in the same buffer.
228+
In this example, only one query will be executed for all story authors comparing to 20 queries
229+
in a naive implementation.
218230

219231
# Async PHP
220-
Since: 0.10.0 (version 0.9.0 had slightly different API which is deprecated)
232+
Since: 0.10.0 (version 0.9.0 had slightly different API which still works, but is deprecated)
221233

222-
If your project runs in environment that supports async operations
234+
If your project runs in an environment that supports async operations
223235
(like HHVM, ReactPHP, Icicle.io, appserver.io, PHP threads, etc)
224236
you can leverage the power of your platform to resolve some fields asynchronously.
225237

@@ -230,6 +242,10 @@ To start using this feature, switch facade method for query execution from
230242
**executeQuery** to **promiseToExecute**:
231243

232244
```php
245+
<?php
246+
use GraphQL\GraphQL;
247+
use GraphQL\Executor\ExecutionResult;
248+
233249
$promise = GraphQL::promiseToExecute(
234250
$promiseAdapter,
235251
$schema,
@@ -252,6 +268,6 @@ Where **$promiseAdapter** is an instance of:
252268
`GraphQL\Executor\Promise\Adapter\ReactPromiseAdapter`
253269

254270
* Other platforms: write your own class implementing interface: <br>
255-
`GraphQL\Executor\Promise\PromiseAdapter`.
271+
[`GraphQL\Executor\Promise\PromiseAdapter`](reference.md#graphqlexecutorpromisepromiseadapter).
256272

257273
Then your **resolve** functions should return promises of your platform instead of `GraphQL\Deferred`s.

docs/error-handling.md

Lines changed: 43 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,47 +1,52 @@
11
# Errors in GraphQL
22

3-
Query execution process never throws exceptions. Instead all errors are caught and collected in
4-
[execution result](executing-queries.md#execution-result).
3+
Query execution process never throws exceptions. Instead, all errors are caught and collected.
4+
After execution, they are available in **$errors** prop of
5+
[`GraphQL\Executor\ExecutionResult`](reference.md#graphqlexecutorexecutionresult).
56

6-
Later `$result->toArray()` automatically converts these errors to array using default
7-
error formatting. But you can apply [custom error filtering and formatting](#custom-error-filtering-and-formatting)
7+
When the result is converted to a serializable array using its **toArray()** method, all errors are
8+
converted to arrays as well using default error formatting (see below).
9+
10+
Alternatively, you can apply [custom error filtering and formatting](#custom-error-handling-and-formatting)
811
for your specific requirements.
912

1013
# Default Error formatting
11-
By default each error entry is converted to associative array with following structure:
14+
By default, each error entry is converted to an associative array with following structure:
1215

1316
```php
17+
<?php
1418
[
1519
'message' => 'Error message',
1620
'category' => 'graphql',
1721
'locations' => [
1822
['line' => 1, 'column' => 2]
1923
],
20-
'path': [
24+
'path' => [
2125
'listField',
2226
0,
2327
'fieldWithException'
2428
]
25-
]
29+
];
2630
```
27-
Entry at key **locations** points to character in query string which caused the error.
28-
In some cases (like deep fragment fields) locations will include several entries to track down path to
29-
field with error in query.
31+
Entry at key **locations** points to a character in query string which caused the error.
32+
In some cases (like deep fragment fields) locations will include several entries to track down
33+
the path to field with the error in query.
3034

31-
Entry at key **path** exists only for errors caused by exceptions thrown in resolvers. It contains path
32-
from the very root field to actual field value producing an error
35+
Entry at key **path** exists only for errors caused by exceptions thrown in resolvers.
36+
It contains a path from the very root field to actual field value producing an error
3337
(including indexes for list types and field names for composite types).
3438

3539
**Internal errors**
3640

37-
As of version **0.10.0** all exceptions thrown in resolvers are reported with generic message **"Internal server error"**.
41+
As of version **0.10.0**, all exceptions thrown in resolvers are reported with generic message **"Internal server error"**.
3842
This is done to avoid information leak in production environments (e.g. database connection errors, file access errors, etc).
3943

40-
Only exceptions implementing interface `GraphQL\Error\ClientAware` and claiming themselves as **safe** will
41-
be reported with full error message.
44+
Only exceptions implementing interface [`GraphQL\Error\ClientAware`](reference.md#graphqlerrorclientaware) and claiming themselves as **safe** will
45+
be reported with a full error message.
4246

4347
For example:
4448
```php
49+
<?php
4550
use GraphQL\Error\ClientAware;
4651

4752
class MySafeException extends \Exception implements ClientAware
@@ -57,20 +62,21 @@ class MySafeException extends \Exception implements ClientAware
5762
}
5863
}
5964
```
60-
When such exception is thrown it will be reported with full error message:
65+
When such exception is thrown it will be reported with a full error message:
6166
```php
67+
<?php
6268
[
6369
'message' => 'My reported error',
6470
'category' => 'businessLogic',
6571
'locations' => [
6672
['line' => 10, 'column' => 2]
6773
],
68-
'path': [
74+
'path' => [
6975
'path',
7076
'to',
7177
'fieldWithException'
7278
]
73-
]
79+
];
7480
```
7581

7682
To change default **"Internal server error"** message to something else, use:
@@ -91,26 +97,29 @@ $result = GraphQL::executeQuery(/*args*/)->toArray($debug);
9197

9298
This will make each error entry to look like this:
9399
```php
100+
<?php
94101
[
95102
'debugMessage' => 'Actual exception message',
96103
'message' => 'Internal server error',
97104
'category' => 'internal',
98105
'locations' => [
99106
['line' => 10, 'column' => 2]
100107
],
101-
'path': [
108+
'path' => [
102109
'listField',
103110
0,
104111
'fieldWithException'
105112
],
106113
'trace' => [
107114
/* Formatted original exception trace */
108115
]
109-
]
116+
];
110117
```
111118

112119
If you prefer first resolver exception to be re-thrown, use following flags:
113120
```php
121+
<?php
122+
use GraphQL\GraphQL;
114123
use GraphQL\Error\Debug;
115124
$debug = Debug::INCLUDE_DEBUG_MESSAGE | Debug::RETHROW_INTERNAL_EXCEPTIONS;
116125

@@ -121,12 +130,14 @@ $result = GraphQL::executeQuery(/*args*/)->toArray($debug);
121130
# Custom Error Handling and Formatting
122131
It is possible to define custom **formatter** and **handler** for result errors.
123132

124-
**Formatter** is responsible for converting instances of `GraphQL\Error\Error` to array.
125-
**Handler** is useful for error filtering and logging.
133+
**Formatter** is responsible for converting instances of [`GraphQL\Error\Error`](reference.md#graphqlerrorerror)
134+
to an array. **Handler** is useful for error filtering and logging.
126135

127-
For example these are default formatter and handler:
136+
For example, these are default formatter and handler:
128137

129138
```php
139+
<?php
140+
use GraphQL\GraphQL;
130141
use GraphQL\Error\Error;
131142
use GraphQL\Error\FormattedError;
132143

@@ -144,7 +155,7 @@ $result = GraphQL::executeQuery(/* $args */)
144155
->toArray();
145156
```
146157

147-
Note that when you pass [debug flags](#debugging-tools) to `toArray()` your custom formatter will still be
158+
Note that when you pass [debug flags](#debugging-tools) to **toArray()** your custom formatter will still be
148159
decorated with same debugging information mentioned above.
149160

150161
# Schema Errors
@@ -155,6 +166,11 @@ Usually such errors mean that there is some logical error in your schema and it
155166
when it makes sense to return `500` error code for GraphQL endpoint:
156167

157168
```php
169+
<?php
170+
use GraphQL\GraphQL;
171+
use GraphQL\Type\Schema;
172+
use GraphQL\Error\FormattedError;
173+
158174
try {
159175
$schema = new Schema([
160176
// ...
@@ -163,9 +179,9 @@ try {
163179
$body = GraphQL::executeQuery($schema, $query);
164180
$status = 200;
165181
} catch(\Exception $e) {
166-
$body = json_encode([
167-
'message' => 'Unexpected error'
168-
]);
182+
$body = [
183+
'errors' => [FormattedError::createFromException($e)]
184+
];
169185
$status = 500;
170186
}
171187

0 commit comments

Comments
 (0)