411 lines
13 KiB
HTML
Raw Normal View History

<!DOCTYPE html>
<!--[if IE 8]><html class="no-js lt-ie9" lang="en" > <![endif]-->
<!--[if gt IE 8]><!--> <html class="no-js" lang="en" > <!--<![endif]-->
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="shortcut icon" href="../img/favicon.ico">
<title>Handling Errors - graphql-php</title>
<link href='https://fonts.googleapis.com/css?family=Lato:400,700|Roboto+Slab:400,700|Inconsolata:400,700' rel='stylesheet' type='text/css'>
<link rel="stylesheet" href="../css/theme.css" type="text/css" />
<link rel="stylesheet" href="../css/theme_extra.css" type="text/css" />
<link rel="stylesheet" href="../css/highlight.css">
<script>
// Current page data
var mkdocs_page_name = "Handling Errors";
var mkdocs_page_input_path = "error-handling.md";
var mkdocs_page_url = "/error-handling/";
</script>
<script src="../js/jquery-2.1.1.min.js"></script>
<script src="../js/modernizr-2.8.3.min.js"></script>
<script type="text/javascript" src="../js/highlight.pack.js"></script>
</head>
<body class="wy-body-for-nav" role="document">
<div class="wy-grid-for-nav">
<nav data-toggle="wy-nav-shift" class="wy-nav-side stickynav">
<div class="wy-side-nav-search">
<a href=".." class="icon icon-home"> graphql-php</a>
<div role="search">
<form id ="rtd-search-form" class="wy-form" action="../search.html" method="get">
<input type="text" name="q" placeholder="Search docs" />
</form>
</div>
</div>
<div class="wy-menu wy-menu-vertical" data-spy="affix" role="navigation" aria-label="main navigation">
<ul class="current">
<li class="toctree-l1">
<a class="" href="..">About</a>
</li>
<li class="toctree-l1">
<a class="" href="../getting-started/">Getting Started</a>
</li>
<li class="toctree-l1">
<a class="" href="../complementary-tools/">Complementary Tools</a>
</li>
<li class="toctree-l1">
<span class="caption-text">Type Definitions</span>
<ul class="subnav">
<li class="">
<a class="" href="../type-system/">Introduction</a>
</li>
<li class="">
<a class="" href="../type-system/object-types/">Object Types</a>
</li>
<li class="">
<a class="" href="../type-system/scalar-types/">Scalar Types</a>
</li>
<li class="">
<a class="" href="../type-system/enum-types/">Enumeration Types</a>
</li>
<li class="">
<a class="" href="../type-system/lists-and-nonnulls/">Lists and Non-Null</a>
</li>
<li class="">
<a class="" href="../type-system/interfaces/">Interfaces</a>
</li>
<li class="">
<a class="" href="../type-system/unions/">Unions</a>
</li>
<li class="">
<a class="" href="../type-system/input-types/">Mutations and Input Types</a>
</li>
<li class="">
<a class="" href="../type-system/directives/">Directives</a>
</li>
<li class="">
<a class="" href="../type-system/schema/">Schema</a>
</li>
<li class="">
<a class="" href="../type-system/type-language/">Using Type Language</a>
</li>
</ul>
</li>
<li class="toctree-l1">
<a class="" href="../executing-queries/">Executing Queries</a>
</li>
<li class="toctree-l1">
<a class="" href="../data-fetching/">Fetching Data</a>
</li>
<li class="toctree-l1 current">
<a class="current" href="./">Handling Errors</a>
<ul class="subnav">
<li class="toctree-l2"><a href="#errors-in-graphql">Errors in GraphQL</a></li>
<li class="toctree-l2"><a href="#default-error-formatting">Default Error formatting</a></li>
<li class="toctree-l2"><a href="#debugging-tools">Debugging tools</a></li>
<li class="toctree-l2"><a href="#custom-error-handling-and-formatting">Custom Error Handling and Formatting</a></li>
<li class="toctree-l2"><a href="#schema-errors">Schema Errors</a></li>
</ul>
</li>
<li class="toctree-l1">
<a class="" href="../security/">Security</a>
</li>
<li class="toctree-l1">
<a class="" href="../how-it-works/">How it works</a>
</li>
<li class="toctree-l1">
<a class="" href="../reference/">Class Reference</a>
</li>
</ul>
</div>
&nbsp;
</nav>
<section data-toggle="wy-nav-shift" class="wy-nav-content-wrap">
<nav class="wy-nav-top" role="navigation" aria-label="top navigation">
<i data-toggle="wy-nav-top" class="fa fa-bars"></i>
<a href="..">graphql-php</a>
</nav>
<div class="wy-nav-content">
<div class="rst-content">
<div role="navigation" aria-label="breadcrumbs navigation">
<ul class="wy-breadcrumbs">
<li><a href="..">Docs</a> &raquo;</li>
<li>Handling Errors</li>
<li class="wy-breadcrumbs-aside">
</li>
</ul>
<hr/>
</div>
<div role="main">
<div class="section">
<h1 id="errors-in-graphql">Errors in GraphQL</h1>
<p>Query execution process never throws exceptions. Instead, all errors are caught and collected.
After execution, they are available in <strong>$errors</strong> prop of
<a href="../reference/#graphqlexecutorexecutionresult"><code>GraphQL\Executor\ExecutionResult</code></a>.</p>
<p>When the result is converted to a serializable array using its <strong>toArray()</strong> method, all errors are
converted to arrays as well using default error formatting (see below). </p>
<p>Alternatively, you can apply <a href="#custom-error-handling-and-formatting">custom error filtering and formatting</a>
for your specific requirements.</p>
<h1 id="default-error-formatting">Default Error formatting</h1>
<p>By default, each error entry is converted to an associative array with following structure:</p>
<pre><code class="php">&lt;?php
[
'message' =&gt; 'Error message',
'category' =&gt; 'graphql',
'locations' =&gt; [
['line' =&gt; 1, 'column' =&gt; 2]
],
'path' =&gt; [
'listField',
0,
'fieldWithException'
]
];
</code></pre>
<p>Entry at key <strong>locations</strong> points to a character in query string which caused the error.
In some cases (like deep fragment fields) locations will include several entries to track down
the path to field with the error in query.</p>
<p>Entry at key <strong>path</strong> exists only for errors caused by exceptions thrown in resolvers.
It contains a path from the very root field to actual field value producing an error
(including indexes for list types and field names for composite types). </p>
<p><strong>Internal errors</strong></p>
<p>As of version <strong>0.10.0</strong>, all exceptions thrown in resolvers are reported with generic message <strong>"Internal server error"</strong>.
This is done to avoid information leak in production environments (e.g. database connection errors, file access errors, etc).</p>
<p>Only exceptions implementing interface <a href="../reference/#graphqlerrorclientaware"><code>GraphQL\Error\ClientAware</code></a> and claiming themselves as <strong>safe</strong> will
be reported with a full error message.</p>
<p>For example:</p>
<pre><code class="php">&lt;?php
use GraphQL\Error\ClientAware;
class MySafeException extends \Exception implements ClientAware
{
public function isClientSafe()
{
return true;
}
public function getCategory()
{
return 'businessLogic';
}
}
</code></pre>
<p>When such exception is thrown it will be reported with a full error message:</p>
<pre><code class="php">&lt;?php
[
'message' =&gt; 'My reported error',
'category' =&gt; 'businessLogic',
'locations' =&gt; [
['line' =&gt; 10, 'column' =&gt; 2]
],
'path' =&gt; [
'path',
'to',
'fieldWithException'
]
];
</code></pre>
<p>To change default <strong>"Internal server error"</strong> message to something else, use: </p>
<pre><code>GraphQL\Error\FormattedError::setInternalErrorMessage(&quot;Unexpected error&quot;);
</code></pre>
<h1 id="debugging-tools">Debugging tools</h1>
<p>During development or debugging use <code>$result-&gt;toArray(true)</code> to add <strong>debugMessage</strong> key to
each formatted error entry. If you also want to add exception trace - pass flags instead:</p>
<pre><code>use GraphQL\Error\Debug;
$debug = Debug::INCLUDE_DEBUG_MESSAGE | Debug::INCLUDE_TRACE;
$result = GraphQL::executeQuery(/*args*/)-&gt;toArray($debug);
</code></pre>
<p>This will make each error entry to look like this:</p>
<pre><code class="php">&lt;?php
[
'debugMessage' =&gt; 'Actual exception message',
'message' =&gt; 'Internal server error',
'category' =&gt; 'internal',
'locations' =&gt; [
['line' =&gt; 10, 'column' =&gt; 2]
],
'path' =&gt; [
'listField',
0,
'fieldWithException'
],
'trace' =&gt; [
/* Formatted original exception trace */
]
];
</code></pre>
<p>If you prefer first resolver exception to be re-thrown, use following flags:</p>
<pre><code class="php">&lt;?php
use GraphQL\GraphQL;
use GraphQL\Error\Debug;
$debug = Debug::INCLUDE_DEBUG_MESSAGE | Debug::RETHROW_INTERNAL_EXCEPTIONS;
// Following will throw if there was an exception in resolver during execution:
$result = GraphQL::executeQuery(/*args*/)-&gt;toArray($debug);
</code></pre>
<h1 id="custom-error-handling-and-formatting">Custom Error Handling and Formatting</h1>
<p>It is possible to define custom <strong>formatter</strong> and <strong>handler</strong> for result errors.</p>
<p><strong>Formatter</strong> is responsible for converting instances of <a href="../reference/#graphqlerrorerror"><code>GraphQL\Error\Error</code></a>
to an array. <strong>Handler</strong> is useful for error filtering and logging. </p>
<p>For example, these are default formatter and handler:</p>
<pre><code class="php">&lt;?php
use GraphQL\GraphQL;
use GraphQL\Error\Error;
use GraphQL\Error\FormattedError;
$myErrorFormatter = function(Error $error) {
return FormattedError::createFromException($error);
};
$myErrorHandler = function(array $errors, callable $formatter) {
return array_map($formatter, $errors);
};
$result = GraphQL::executeQuery(/* $args */)
-&gt;setErrorFormatter($myErrorFormatter)
-&gt;setErrorsHandler($myErrorHandler)
-&gt;toArray();
</code></pre>
<p>Note that when you pass <a href="#debugging-tools">debug flags</a> to <strong>toArray()</strong> your custom formatter will still be
decorated with same debugging information mentioned above.</p>
<h1 id="schema-errors">Schema Errors</h1>
<p>So far we only covered errors which occur during query execution process. But schema definition can
also throw <code>GraphQL\Error\InvariantViolation</code> if there is an error in one of type definitions.</p>
<p>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 <code>500</code> error code for GraphQL endpoint:</p>
<pre><code class="php">&lt;?php
use GraphQL\GraphQL;
use GraphQL\Type\Schema;
use GraphQL\Error\FormattedError;
try {
$schema = new Schema([
// ...
]);
$body = GraphQL::executeQuery($schema, $query);
$status = 200;
} catch(\Exception $e) {
$body = [
'errors' =&gt; [FormattedError::createFromException($e)]
];
$status = 500;
}
header('Content-Type: application/json', true, $status);
echo json_encode($body);
</code></pre>
</div>
</div>
<footer>
<div class="rst-footer-buttons" role="navigation" aria-label="footer navigation">
<a href="../security/" class="btn btn-neutral float-right" title="Security">Next <span class="icon icon-circle-arrow-right"></span></a>
<a href="../data-fetching/" class="btn btn-neutral" title="Fetching Data"><span class="icon icon-circle-arrow-left"></span> Previous</a>
</div>
<hr/>
<div role="contentinfo">
<!-- Copyright etc -->
</div>
Built with <a href="http://www.mkdocs.org">MkDocs</a> using a <a href="https://github.com/snide/sphinx_rtd_theme">theme</a> provided by <a href="https://readthedocs.org">Read the Docs</a>.
</footer>
</div>
</div>
</section>
</div>
<div class="rst-versions" role="note" style="cursor: pointer">
<span class="rst-current-version" data-toggle="rst-current-version">
<span><a href="../data-fetching/" style="color: #fcfcfc;">&laquo; Previous</a></span>
<span style="margin-left: 15px"><a href="../security/" style="color: #fcfcfc">Next &raquo;</a></span>
</span>
</div>
<script>var base_url = '..';</script>
<script src="../js/theme.js"></script>
<script src="../search/require.js"></script>
<script src="../search/search.js"></script>
</body>
</html>