mirror of
https://github.com/retailcrm/graphql-php.git
synced 2024-11-25 14:26:08 +03:00
Work in progress on better docs (added section on query execution and error handling)
This commit is contained in:
parent
24b2285ffe
commit
ccad34517c
136
docs/error-handling.md
Normal file
136
docs/error-handling.md
Normal file
@ -0,0 +1,136 @@
|
|||||||
|
# Errors in GraphQL
|
||||||
|
|
||||||
|
Query execution process never throws exceptions. Instead all errors that occur during query execution
|
||||||
|
are caught, collected and included in response.
|
||||||
|
|
||||||
|
There are 3 types of errors in GraphQL (Syntax, Validation and Execution errors):
|
||||||
|
|
||||||
|
**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',
|
||||||
|
'locations' => [
|
||||||
|
['line' => 1, 'column' => 2]
|
||||||
|
],
|
||||||
|
'path': [
|
||||||
|
'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 - it will be bubbled up to first nullable field which will
|
||||||
|
be replaced with `null` (and error entry added to response). If all fields up to the root are non-null
|
||||||
|
**data** entry will be missing in response and only **errors** key will be presented.
|
||||||
|
|
||||||
|
# Debugging tools
|
||||||
|
|
||||||
|
Each error entry contains pointer to line and column in original query string which caused
|
||||||
|
the error:
|
||||||
|
|
||||||
|
```php
|
||||||
|
'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
|
||||||
|
'fieldWithException'
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
# Custom Error Formatting
|
||||||
|
|
||||||
|
If you want to apply custom formatting to errors - use **GraphQL::executeAndReturnResult()** instead
|
||||||
|
of **GraphQL::execute()**.
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
# Schema Errors
|
||||||
|
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.
|
||||||
|
|
||||||
|
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:
|
||||||
|
|
||||||
|
```php
|
||||||
|
try {
|
||||||
|
$schema = new Schema([
|
||||||
|
// ...
|
||||||
|
]);
|
||||||
|
|
||||||
|
$result = GraphQL::execute($schema, $query);
|
||||||
|
} catch(\Exception $e) {
|
||||||
|
header('Content-Type: application/json', true, 500);
|
||||||
|
echo json_encode([
|
||||||
|
'message' => 'Unexpected error'
|
||||||
|
]);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
```
|
49
docs/executing-queries.md
Normal file
49
docs/executing-queries.md
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
# Overview
|
||||||
|
Query execution is a complex process involving multiple steps, including query **parsing**,
|
||||||
|
**validating** and finally **executing** against your schema.
|
||||||
|
|
||||||
|
**graphql-php** provides convenient facade for this process in class `GraphQL\GraphQL`:
|
||||||
|
|
||||||
|
```php
|
||||||
|
use GraphQL\GraphQL;
|
||||||
|
|
||||||
|
$result = GraphQL::execute(
|
||||||
|
$schema,
|
||||||
|
$queryString,
|
||||||
|
$rootValue = null,
|
||||||
|
$contextValue = null,
|
||||||
|
$variableValues = null,
|
||||||
|
$operationName = 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/).
|
||||||
|
|
||||||
|
|
||||||
|
Description of method arguments:
|
||||||
|
|
||||||
|
Argument | Type | Notes
|
||||||
|
------------ | -------- | -----
|
||||||
|
schema | `GraphQL\Schema` | **Required.** Instance of your application [Schema](type-system/schema/)
|
||||||
|
queryString | `string` or `GraphQL\Language\AST\Document` | **Required.** Actual GraphQL query string to be parsed, validated and executed. If you parse query elsewhere before executing - pass corresponding ast document here to avoid new parsing.
|
||||||
|
rootValue | `mixed` | Any value that represents a root of your data graph. It is passed as 1st argument to field resolvers of [Query type](type-system/schema/#query-and-mutation-types). Can be omitted or set to null if actual root values are fetched by Query type itself.
|
||||||
|
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.
|
||||||
|
|
||||||
|
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
|
||||||
|
on [Error Handling](error-handling/) for essentials.
|
||||||
|
|
||||||
|
|
||||||
|
# Parsing
|
||||||
|
TODOC
|
||||||
|
|
||||||
|
# Validating
|
||||||
|
TODOC
|
||||||
|
|
||||||
|
# Executing
|
||||||
|
TODOC
|
@ -1,6 +1,6 @@
|
|||||||
# Built-in directives
|
# Built-in directives
|
||||||
Directive is a way to dynamically change the structure and shape of queries using variables.
|
Directive is a way for client to give GraphQL server additional context and hints on how to execute
|
||||||
Directive can be attached to a field or fragment inclusion, and can affect execution of the
|
the query. Directive can be attached to a field or fragment inclusion, and can affect execution of the
|
||||||
query in any way the server desires.
|
query in any way the server desires.
|
||||||
|
|
||||||
GraphQL specification includes two built-in directives:
|
GraphQL specification includes two built-in directives:
|
||||||
@ -21,7 +21,7 @@ query Hero($episode: Episode, $withFriends: Boolean!) {
|
|||||||
```
|
```
|
||||||
Here if `$withFriends` variable is set to `false` - friends section will be ignored and excluded
|
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
|
from response. Important implementation detail: those fields will never be executed
|
||||||
(and not just removed from response after execution).
|
(not just removed from response after execution).
|
||||||
|
|
||||||
# Custom directives
|
# Custom directives
|
||||||
**graphql-php** supports custom directives even though their presence does not affect execution of fields.
|
**graphql-php** supports custom directives even though their presence does not affect execution of fields.
|
||||||
|
@ -50,6 +50,12 @@ class QueryType extends BaseType
|
|||||||
'type' => Types::string(),
|
'type' => Types::string(),
|
||||||
'deprecationReason' => 'This field is deprecated!'
|
'deprecationReason' => 'This field is deprecated!'
|
||||||
],
|
],
|
||||||
|
'fieldWithException' => [
|
||||||
|
'type' => Types::string(),
|
||||||
|
'resolve' => function() {
|
||||||
|
throw new \Exception("Exception message thrown in field resolver");
|
||||||
|
}
|
||||||
|
],
|
||||||
'hello' => Type::string()
|
'hello' => Type::string()
|
||||||
],
|
],
|
||||||
'resolveField' => function($val, $args, $context, ResolveInfo $info) {
|
'resolveField' => function($val, $args, $context, ResolveInfo $info) {
|
||||||
|
@ -46,9 +46,7 @@ try {
|
|||||||
$data += ['query' => null, 'variables' => null];
|
$data += ['query' => null, 'variables' => null];
|
||||||
|
|
||||||
if (null === $data['query']) {
|
if (null === $data['query']) {
|
||||||
$data['query'] = '
|
$data['query'] = '{hello}';
|
||||||
{hello}
|
|
||||||
';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// GraphQL schema to be passed to query executor:
|
// GraphQL schema to be passed to query executor:
|
||||||
|
@ -13,7 +13,9 @@ pages:
|
|||||||
- Input Types: type-system/input-types.md
|
- Input Types: type-system/input-types.md
|
||||||
- Directives: type-system/directives.md
|
- Directives: type-system/directives.md
|
||||||
- Defining Schema: type-system/schema.md
|
- Defining Schema: type-system/schema.md
|
||||||
- Data Fetching: data-fetching.md
|
- Executing Queries: executing-queries.md
|
||||||
|
- Handling Errors: error-handling.md
|
||||||
|
- Fetching Data: data-fetching.md
|
||||||
- Best Practices: best-practices.md
|
- Best Practices: best-practices.md
|
||||||
- Complementary Tools: complementary-tools.md
|
- Complementary Tools: complementary-tools.md
|
||||||
theme: readthedocs
|
theme: readthedocs
|
||||||
|
Loading…
Reference in New Issue
Block a user