From cb288948ae4dae48b58a5392e9c9bdb845051b52 Mon Sep 17 00:00:00 2001 From: jekabskarklins Date: Fri, 17 Jul 2026 11:28:37 +0200 Subject: [PATCH] docs: add documentation for error handling and update navigation --- .../docs/developer-guide/error_handling.md | 59 +++++++++++++++++++ documentation/mkdocs.yml | 1 + 2 files changed, 60 insertions(+) create mode 100644 documentation/docs/developer-guide/error_handling.md diff --git a/documentation/docs/developer-guide/error_handling.md b/documentation/docs/developer-guide/error_handling.md new file mode 100644 index 0000000000..f6460ee4fd --- /dev/null +++ b/documentation/docs/developer-guide/error_handling.md @@ -0,0 +1,59 @@ +# Error Handling + +_________________________________________________________________________________________________________ + +As a rule of thumb, errors in the backend are not handled where they happen. Datasources and mutations let errors travel up to Apollo Server, which logs them in one central place and strips sensitive details before the response reaches the client. + +## Two kinds of errors + +**Expected failures** — a missing record, a permission problem, invalid input most often occuring in the Mutation layer. Return a `Rejection` instead of throwing: + + if (!invite) { + return rejection('Invite code not found', { invite: code }); + } + +`Rejection` extends `GraphQLError`, and GraphQL turns any `Error` a resolver returns into an entry in the response's `errors` array. So a returned rejection reaches the client without a `throw`. + +**Unexpected failures** — a failing query, a bug. Just let them throw. Datasources can add a bit of context to the message and re-throw if needed: + + .catch((error: Error) => { + throw new Error(`Could not find invites: ${error.message}`); + }) + +Note. A caught error returns a success response, so the client believes the operation worked. + +_________________________________________________________________________________________________________ + +## Central logging with Apollo plugin + +An Apollo plugin in `src/middlewares/graphql.ts` hooks `didEncounterErrors` and logs every error in the response, along with the requesting user's id: + + logger.logError('GraphQL response contained error(s)', { + errors: filteredErrors, + context, + }); + +Expired JWTs and invalid external tokens are filtered out first and logged as a warning and an info respectively, since they are routine and would otherwise drown out real errors. + +Because this plugin exists, resolvers, mutations and datasources do not need to log errors themselves. + +## Hiding details from the client in production + +`formatError`, in the same file, deletes `extensions.context` and `extensions.exception` when running in production. The full detail stays in the server log; the client only sees the message. + +_________________________________________________________________________________________________________ + +## Logging an error yourself + +Use `logException` when you have an `Error` object, and `logError` when you do not: + + logger.logException('Could not download file', e, { req }); + logger.logError('Something went wrong', { someId }); + +Both log at `ERROR` level. The difference is that `logException` takes the error as a separate argument and copies its `name`, `message` and `stack` into the log context. This matters: an `Error` passed into `logError` directly serialises to `{}` and its message and stack trace are lost. + +## Outside GraphQL + +The file and PDF factory endpoints are plain Express routes and never reach the Apollo plugin. They use their own handlers (`exceptionHandler.ts`, `factory.ts`), which log the exception and return a 500. + +_________________________________________________________________________________________________________ diff --git a/documentation/mkdocs.yml b/documentation/mkdocs.yml index 0f468aaf4a..b533006c25 100644 --- a/documentation/mkdocs.yml +++ b/documentation/mkdocs.yml @@ -77,6 +77,7 @@ nav: - RabbitMQ: developer-guide/rabbitmq.md - Kubernetes: developer-guide/kubernetes.md - Depencency Injection: developer-guide/dependency_injection.md + - Error Handling: developer-guide/error_handling.md - Email Service: developer-guide/email.md - Documentation Changes: developer-guide/documentation_changes.md - Technique Proposals: developer-guide/technique_proposals.md