diff --git a/vuepress/docs/docs/middleware.md b/vuepress/docs/docs/middleware.md index 62cde6ef9a..c1f03b9fd6 100644 --- a/vuepress/docs/docs/middleware.md +++ b/vuepress/docs/docs/middleware.md @@ -92,4 +92,32 @@ val i4: GraphQLInterpreter[MyEnv with Clock, CalibanError] = _.getOrElse(GraphQLResponse(NullValue, List(ExecutionError("Timeout!")))) ) ) +``` + +## Customizing error responses + +During various phases of executing a query, an error may occur. Caliban renders the different instances of `CalibanError` to a GraphQL spec compliant response. As a user, you will most likely encounter `ExecutionError` at some point because this will encapsulate the errors in the error channel of your effects. For Caliban to be able to render some basic message about the error that occured during query execution, it is important that your errror extends `Throwable`. + +For more meaningful error handling, GraphQL spec allows for an [`extension`](http://spec.graphql.org/June2018/#example-fce18) object in the error response. This object may include, for instance, `code` information to model enum-like error codes that can be handled by a front-end. In order to generate this information, one can use the `mapError` function on a `GraphQLInterpreter`. An example is provided below in which we map a custom domain error within an `ExecutionError` to a meaningful error code. + +```scala +sealed trait ExampleAppEncodableError extends Throwable { + def errorCode: String +} +case object UnauthorizedError extends ExampleAppEncodableError { + override def errorCode: String = "UNAUTHORIZED" +} + +def withErrorCodeExtensions[R]( + interpreter: GraphQLInterpreter[R, CalibanError] +): GraphQLInterpreter[R, CalibanError] = interpreter.mapError { + case err @ ExecutionError(_, _, _, Some(exampleError: ExampleAppEncodableError), _) => + err.copy(extensions = Some(ObjectValue(List(("errorCode", StringValue(exampleError.errorCode)))))) + case err: ExecutionError => + err.copy(extensions = Some(ObjectValue(List(("errorCode", StringValue("EXECUTION_ERROR")))))) + case err: ValidationError => + err.copy(extensions = Some(ObjectValue(List(("errorCode", StringValue("VALIDATION_ERROR")))))) + case err: ParsingError => + err.copy(extensions = Some(ObjectValue(List(("errorCode", StringValue("PARSING_ERROR")))))) +} ``` \ No newline at end of file