Skip to content

Commit 25e31b1

Browse files
authored
[GraphQL] Exception formatting (#986)
* Add documentation about exception formatting * Update core/graphql.md
1 parent c22f064 commit 25e31b1

File tree

1 file changed

+76
-0
lines changed

1 file changed

+76
-0
lines changed

core/graphql.md

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -929,6 +929,82 @@ For instance:
929929
- If you use a different `normalization_context` for a mutation, a `MyResourcePayloadData` type with the restricted fields will be generated and used instead of `MyResource` (the query type).
930930
- If you use a different `normalization_context` for the query of an item (`item_query` operation) and for the query of a collection (`collection_query` operation), two types `MyResourceItem` and `MyResourceCollection` with the restricted fields will be generated and used instead of `MyResource` (the query type).
931931

932+
## Exception and Error Formatting
933+
934+
By default, if an exception is sent when resolving a query or a mutation, it is normalized following the [GraphQL specification](https://github.com/graphql/graphql-spec/blob/master/spec/Section%207%20--%20Response.md#errors).
935+
936+
It means an `errors` entry will be returned in the response, containing the following entries: `message`, `extensions`, `locations` and `path`.
937+
For more information, please [refer to the documentation in graphql-php](https://webonyx.github.io/graphql-php/error-handling/#default-error-formatting).
938+
939+
In `prod` mode, the displayed message will be a generic one, excepted for a `RuntimeException` (and all exceptions inherited from it) for which it will be its actual message.
940+
This behavior is different from what is described in the [graphql-php documentation](https://webonyx.github.io/graphql-php/error-handling).
941+
It's because a built-in [custom exception normalizer](#custom-exception-normalizer) is used to normalize the `RuntimeException` and change the default behavior.
942+
943+
If you are in `dev` mode, more entries will be added in the response: `debugMessage` (containing the actual exception message, for instance in the case of a `LogicException`) and `trace` (the formatted exception trace).
944+
945+
For some specific exceptions, built-in [custom exception normalizers](#custom-exception-normalizer) are also used to add more information.
946+
It's the case for a `HttpException` for which the `status` entry will be added under `extensions` and for a `ValidationException` for which `status` (always 400) and `violations` entries will be added.
947+
948+
### Custom Exception Normalizer
949+
950+
If you want to add more specific behaviors depending on the exception or if you want to change the behavior of the built-in ones, you can do so by creating your own normalizer.
951+
952+
Please follow the [Symfony documentation to create a custom normalizer](https://symfony.com/doc/current/serializer/custom_normalizer.html).
953+
954+
The code should look like this:
955+
956+
```php
957+
<?php
958+
// api/src/Serializer/Exception/MyExceptionNormalizer.php
959+
960+
namespace App\Serializer\Exception;
961+
962+
use App\Exception\MyException;
963+
use GraphQL\Error\Error;
964+
use GraphQL\Error\FormattedError;
965+
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
966+
967+
final class MyExceptionNormalizer implements NormalizerInterface
968+
{
969+
/**
970+
* {@inheritdoc}
971+
*/
972+
public function normalize($object, $format = null, array $context = []): array
973+
{
974+
$exception = $object->getPrevious();
975+
$error = FormattedError::createFromException($object);
976+
977+
// Add your logic here and add your specific data in the $error array (in the 'extensions' entry to follow the GraphQL specification).
978+
// $error['extensions']['yourEntry'] = ...;
979+
980+
return $error;
981+
}
982+
983+
/**
984+
* {@inheritdoc}
985+
*/
986+
public function supportsNormalization($data, $format = null): bool
987+
{
988+
return $data instanceof Error && $data->getPrevious() instanceof MyException;
989+
}
990+
}
991+
```
992+
993+
You can see that, in the `normalize` method, you should add a call to `FormattedError::createFromException` in order to have the same behavior as the other normalizers.
994+
995+
When registering your custom normalizer, you can add a priority to order your normalizers between themselves.
996+
997+
If you use a positive priority (or no priority), your normalizer will always be called before the built-in normalizers.
998+
For instance, you can register a custom normalizer like this:
999+
1000+
```yaml
1001+
# api/config/services.yaml
1002+
services:
1003+
App\Serializer\Exception\MyExceptionNormalizer:
1004+
tags:
1005+
- { name: 'serializer.normalizer', priority: 12 }
1006+
```
1007+
9321008
## Name Conversion
9331009

9341010
You can modify how the property names of your resources are converted into field and filter names of your GraphQL schema.

0 commit comments

Comments
 (0)