Documentation improvements (wip)

This commit is contained in:
Vladimir Razuvaev 2017-08-17 02:17:01 +07:00
parent b294329a40
commit a2be92937e
5 changed files with 197 additions and 133 deletions

View File

@ -4,7 +4,7 @@ plain files or in-memory data structures.
In order to convert GraphQL query to PHP array **graphql-php** traverses query fields (using depth-first algorithm) and
runs special `resolve` function on each field. This `resolve` function is provided by you as a part of
[field definition](type-system/object-types/#field-configuration-options).
[field definition](type-system/object-types/#field-configuration-options) or [query execution call](executing-queries/#overview).
Result returned by `resolve` function is directly included in response (for scalars and enums)
or passed down to nested fields (for objects).

View File

@ -1,143 +1,145 @@
# Errors in GraphQL
Query execution process never throws exceptions. Instead all errors that occur during query execution
are caught, collected and included in response.
Query execution process never throws exceptions. Instead all errors are caught and collected in
[execution result](executing-queries/#execution-result).
There are 3 types of errors in GraphQL (Syntax, Validation and Execution errors):
Later `$result->toArray()` automatically converts these errors to array using default
error formatting. But you can apply [custom error filtering and formatting](#custom-error-filtering-and-formatting)
for your specific requirements.
# Default Error formatting
By default each error entry is converted to associative array with following structure:
**Syntax** errors are returned in response when query has invalid syntax and could not be parsed.
Example output for invalid query `{hello` (missing bracket):
```php
[
'errors' => [
[
'message' => "Syntax Error GraphQL request (1:7) Expected Name, found <EOF>\n\n1: {hello\n ^\n",
'locations' => [
['line' => 1, 'column' => 7]
]
]
]
]
```
**Validation** errors - returned in response when query has semantic errors.
Example output for invalid query `{unknownField}`:
```php
[
'errors' => [
[
'message' => 'Cannot query field "unknownField" on type "Query".',
'locations' => [
['line' => 1, 'column' => 2]
]
]
]
]
```
**Execution** errors - included in response when some field resolver throws
(or returns unexpected value). Example output for query with exception thrown in
field resolver `{fieldWithException}`:
```php
[
'data' => [
'fieldWithException' => null
],
'errors' => [
[
'message' => 'Exception message thrown in field resolver',
'message' => 'Error message',
'category' => 'graphql',
'locations' => [
['line' => 1, 'column' => 2]
],
'path': [
'listField',
0,
'fieldWithException'
]
]
```
Entry at key **locations** points to character in query string which caused the error.
In some cases (like deep fragment fields) locations will include several entries to track down path to
field with error in query.
Entry at key **path** exists only for errors caused by exceptions thrown in resolvers. It contains path
from the very root field to actual field value producing an error
(including indexes for list types and field names for composite types).
**Internal errors**
As of version **0.10.0** all exceptions thrown in resolvers are reported with generic message **"Internal server error"**.
This is done to avoid information leak in production environments (e.g. database connection errors, file access errors, etc).
Only exceptions implementing interface `GraphQL\Error\ClientAware` and claiming themselves as **safe** will
be reported with full error message.
For example:
```php
use GraphQL\Error\ClientAware;
class MySafeException extends \Exception implements ClientAware
{
public function isClientSafe()
{
return true;
}
public function getCategory()
{
return 'businessLogic';
}
}
```
When such exception is thrown it will be reported with full error message:
```php
[
'message' => 'My reported error',
'category' => 'businessLogic',
'locations' => [
['line' => 10, 'column' => 2]
],
'path': [
'path',
'to',
'fieldWithException'
]
]
```
Obviously when **Syntax** or **Validation** error is detected - process is interrupted and query is not
executed. In such scenarios response only contains **errors**, but not **data**.
GraphQL is forgiving to **Execution** errors which occur in resolvers of nullable fields.
If such field throws or returns unexpected value the value of the field in response will be simply
replaced with `null` and error entry will be added to response.
If exception is thrown in non-null field - error bubbles up to first nullable field. This nullable field is
replaced with `null` and error entry is added to response. If all fields up to the root are non-null -
**data** entry will be removed from response and only **errors** key will be presented.
To change default **"Internal server error"** message to something else, use:
```
GraphQL\Error\FormattedError::setInternalErrorMessage("Unexpected error");
```
#Debugging tools
Each error entry contains pointer to line and column in original query string which caused
the error:
During development or debugging use `$result->toArray(true)` to add **debugMessage** key to
each formatted error entry. If you also want to add exception trace - pass flags instead:
```
use GraphQL\Error\FormattedError;
$debug = FormattedError::INCLUDE_DEBUG_MESSAGE | FormattedError::INCLUDE_TRACE;
$result = GraphQL::executeQuery(/*args*/)->toArray($debug);
```
This will make each error entry to look like this:
```php
[
'message' => 'Internal server error',
'debugMessage' => 'Actual exception message',
'category' => 'internal',
'locations' => [
['line' => 1, 'column' => 2]
]
```
GraphQL clients like **Relay** or **GraphiQL** leverage this information to highlight
actual piece of query containing error.
In some cases (like deep fragment fields) locations will include several entries to track down the
path to field with error in query.
**Execution** errors also contain **path** from the very root field to actual field value producing
an error (including indexes for array types and fieldNames for object types). So in complex situation
this path could look like this:
```php
'path' => [
'lastStoryPosted',
'author',
'friends',
3
['line' => 10, 'column' => 2]
],
'path': [
'listField',
0,
'fieldWithException'
],
'trace' => [
/* Formatted original exception trace */
]
]
```
# Custom Error Formatting
# Custom Error Handling and Formatting
It is possible to define custom **formatter** and **handler** for result errors.
If you want to apply custom formatting to errors - use **GraphQL::executeAndReturnResult()** instead
of **GraphQL::execute()**.
**Formatter** is responsible for converting instances of `GraphQL\Error\Error` to array.
**Handler** is useful for error filtering and logging.
It has exactly the same [signature](executing-queries/), but instead of array it
returns `GraphQL\Executor\ExecutionResult` instance which holds errors in public **$errors**
property and data in **$data** property.
Each entry of **$errors** array contains instance of `GraphQL\Error\Error` which wraps original
exceptions thrown by resolvers. To access original exceptions use `$error->getPrevious()` method.
But note that previous exception is only available for **Execution** errors and will be `null`
for **Syntax** or **Validation** errors.
For example:
For example these are default formatter and handler:
```php
$result = GraphQL::executeAndReturnResult()
->setErrorFormatter(function(GraphQL\Error\Error $err) {
$resolverException = $err->getPrevious();
use GraphQL\Error\Error;
use GraphQL\Error\FormattedError;
if ($resolverException instanceof MyResolverException) {
$formattedError = [
'message' => $resolverException->getMessage(),
'code' => $resolverException->getCode()
];
} else {
$formattedError = [
'message' => $err->getMessage()
];
}
return $formattedError;
})
$myErrorFormatter = function(Error $error) {
return FormattedError::createFromException($error);
};
$myErrorHandler = function(array $errors, callable $formatter) {
return array_map($formatter, $errors);
};
$result = GraphQL::executeQuery(/* $args */)
->setErrorFormatter($myErrorFormatter)
->setErrorHandler($myErrorHandler)
->toArray();
```
You may also re-throw exceptions in result handler for debugging, etc.
# Schema Errors
So far we only covered errors which occur during query execution process. But schema definition can
also throw if there is an error in one of type definitions.
also throw `GraphQL\Error\InvariantViolation` if there is an error in one of type definitions.
Usually such errors mean that there is some logical error in your schema and it is the only case
when it makes sense to return `500` error code for GraphQL endpoint:
@ -148,7 +150,7 @@ try {
// ...
]);
$body = GraphQL::execute($schema, $query);
$body = GraphQL::executeQuery($schema, $query);
$status = 200;
} catch(\Exception $e) {
$body = json_encode([

View File

@ -2,26 +2,36 @@
Query execution is a complex process involving multiple steps, including query **parsing**,
**validating** and finally **executing** against your [schema](type-system/schema/).
**graphql-php** provides convenient facade for this process in class `GraphQL\GraphQL`:
**graphql-php** provides convenient facade for this process in class
[`GraphQL\GraphQL`](/reference/#graphqlgraphql):
```php
use GraphQL\GraphQL;
$result = GraphQL::execute(
$result = GraphQL::executeQuery(
$schema,
$queryString,
$rootValue = null,
$contextValue = null,
$variableValues = null,
$operationName = null
$operationName = null,
$fieldResolver = null,
$validationRules = null,
$promiseAdapter = null
);
```
Method returns `array` with **data** and **errors** keys, as described by
[GraphQL specs](http://facebook.github.io/graphql/#sec-Response-Format).
This array is suitable for further serialization (e.g. using `json_encode`).
See also section on [error handling](error-handling/).
It returns an instance of [`GraphQL\Executor\ExecutionResult`](/reference/#graphqlexecutorexecutionresult)
which can be easily converted to array:
```php
$serializableResult = $result->toArray();
```
Returned array contains **data** and **errors** keys, as described by
[GraphQL spec](http://facebook.github.io/graphql/#sec-Response-Format).
This array is suitable for further serialization (e.g. using `json_encode`).
See also section on [error handling and formatting](error-handling/).
Description of method arguments:
@ -33,10 +43,39 @@ rootValue | `mixed` | Any value that represents a root of your data graph. It i
contextValue | `mixed` | Any value that holds information shared between all field resolvers. Most often they use it to pass currently logged in user, locale details, etc.<br><br>It will be available as 3rd argument in all field resolvers. (see section on [Field Definitions](type-system/object-types/#field-configuration-options) for reference) **graphql-php** never modifies this value and passes it *as is* to all underlying resolvers.
variableValues | `array` | Map of variable values passed along with query string. See section on [query variables on official GraphQL website](http://graphql.org/learn/queries/#variables)
operationName | `string` | Allows the caller to specify which operation in queryString will be run, in cases where queryString contains multiple top-level operations.
fieldResolver | `callable` | A resolver function to use when one is not provided by the schema. If not provided, the [default field resolver is used](data-fetching/#default-field-resolver).
validationRules | `array` | A set of rules for query validation step. Default value is all available rules. Empty array would allow to skip query validation (may be convenient for persisted queries which are validated before persisting and assumed valid during execution)
promiseAdapter | `GraphQL\Executir\Promise\PromiseAdapter` | Adapter for async-enabled PHP platforms like ReactPHP (read about [async platforms integration](data-fetching/#async-php))
# Execution Result
```php
namespace GraphQL\Executor;
class ExecutionResult
{
/**
* @var array
*/
public $data;
/**
* @var GraphQL\Error\Error[]
*/
public $errors;
/**
*
*/
public setErrorsHandler(callable $errorsHandler);
public setErrorFormatter(callable $errorsHandler);
}
```
# Parsing
Following reading describes implementation details of query execution process. It may clarify some
internals of GraphQL but is not required in order to use it. Feel free to skip to next section
internals of GraphQL but is not required to use it. Feel free to skip to next section
on [Error Handling](error-handling/) for essentials.
TODOC
@ -46,3 +85,21 @@ TODOC
# Executing
TODOC
# Errors explained
There are 3 types of errors in GraphQL:
- **Syntax**: query has invalid syntax and could not be parsed;
- **Validation**: query is incompatible with type system (e.g. unknown field is requested);
- **Execution**: occurs when some field resolver throws (or returns unexpected value).
Obviously when **Syntax** or **Validation** error is detected - process is interrupted and query is not
executed.
GraphQL is forgiving to **Execution** errors which occur in resolvers of nullable fields.
If such field throws or returns unexpected value the value of the field in response will be simply
replaced with `null` and error entry will be registered.
If exception is thrown in non-null field - error bubbles up to first nullable field. This nullable field is
replaced with `null` and error entry is added to response. If all fields up to the root are non-null -
**data** entry will be removed from response and only **errors** key will be presented.

View File

@ -88,12 +88,14 @@ $variableValues = isset($input['variables']) ? $input['variables'] : null;
try {
$rootValue = ['prefix' => 'You said: '];
$result = GraphQL::execute($schema, $query, $rootValue, null, $variableValues);
$result = GraphQL::executeQuery($schema, $query, $rootValue, null, $variableValues);
} catch (\Exception $e) {
$result = [
'error' => [
'errors' => [
[
'message' => $e->getMessage()
]
]
];
}
header('Content-Type: application/json; charset=UTF-8');

View File

@ -2,16 +2,18 @@
Schema is a container of your type hierarchy, which accepts root types in constructor and provides
methods for receiving information about your types to internal GrahpQL tools.
In **graphql-php** schema is an instance of `GraphQL\Type\Schema` which accepts configuration array
in constructor:
In **graphql-php** schema is an instance of [`GraphQL\Type\Schema`](/reference/#graphqltypeschema)
which accepts configuration array in constructor:
```php
use GraphQL\Type\Schema;
$schema = new Schema([
'query' => $queryType,
'mutation' => $mutationType,
]);
```
See possible constructor options [below](#configuration-options)
See possible constructor options [below](#configuration-options).
# Query and Mutation types
Schema consists of two root types:
@ -76,7 +78,8 @@ Field names of Mutation type are usually verbs and they almost always have argum
with complex input values (see [Input Types](input-types/) for details).
# Configuration Options
Schema constructor expects an array with following options:
Schema constructor expects an instance of [`GraphQL\Type\SchemaConfig`](/reference/#graphqltypeschemaconfig)
or an array with following options:
Option | Type | Notes
------------ | -------- | -----