mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-07-29 08:09:52 +08:00
refactor(server): server errors (#5741)
standardize the error raising in both GraphQL Resolvers and Controllers.
Now, All user aware errors should be throwed with `HttpException`'s variants, for example `NotFoundException`.
> Directly throwing `GraphQLError` are forbidden.
The GraphQL errorFormatter will handle it automatically and set `code`, `status` in error extensions.
At the same time, the frontend `GraphQLError` should be imported from `@affine/graphql`, which introduce a better error extensions type.
----
controller example:
```js
@Get('/docs/${id}')
doc() {
// ...
// imported from '@nestjs/common'
throw new NotFoundException('Doc is not found.');
// ...
}
```
the above will response as:
```
status: 404 Not Found
{
"message": "Doc is not found.",
"statusCode": 404,
"error": "Not Found"
}
```
resolver example:
```js
@Mutation()
invite() {
// ...
throw new PayloadTooLargeException('Workspace seats is full.')
// ...
}
```
the above will response as:
```
status: 200 Ok
{
"data": null,
"errors": [
{
"message": "Workspace seats is full.",
"extensions": {
"code": 404,
"status": "Not Found"
}
}
]
}
```
for frontend GraphQLError user-friend, a helper function introduced:
```js
import { findGraphQLError } from '@affine/graphql'
fetch(query)
.catch(errOrArr => {
const e = findGraphQLError(errOrArr, e => e.extensions.code === 404)
if (e) {
// handle
}
})
```
This commit is contained in:
@@ -1,53 +0,0 @@
|
||||
import {
|
||||
ArgumentsHost,
|
||||
Catch,
|
||||
ExceptionFilter,
|
||||
HttpException,
|
||||
Logger,
|
||||
NotFoundException,
|
||||
} from '@nestjs/common';
|
||||
import { GqlContextType } from '@nestjs/graphql';
|
||||
import { Request, Response } from 'express';
|
||||
|
||||
const TrivialExceptions = [NotFoundException];
|
||||
|
||||
export const REQUEST_ID_HEADER = 'x-request-id';
|
||||
|
||||
@Catch()
|
||||
export class ExceptionLogger implements ExceptionFilter {
|
||||
private readonly logger = new Logger('ExceptionLogger');
|
||||
|
||||
catch(exception: Error, host: ArgumentsHost) {
|
||||
// with useGlobalFilters, the context is always HTTP
|
||||
const ctx = host.switchToHttp();
|
||||
|
||||
const request = ctx.getRequest<Request>();
|
||||
const requestId = request?.header(REQUEST_ID_HEADER);
|
||||
|
||||
const shouldVerboseLog = !TrivialExceptions.some(
|
||||
e => exception instanceof e
|
||||
);
|
||||
this.logger.error(
|
||||
new Error(
|
||||
`${requestId ? `requestId-${requestId}: ` : ''}${exception.message}${
|
||||
shouldVerboseLog ? '\n' + exception.stack : ''
|
||||
}`,
|
||||
{ cause: exception }
|
||||
)
|
||||
);
|
||||
|
||||
if (host.getType<GqlContextType>() === 'graphql') {
|
||||
return;
|
||||
}
|
||||
|
||||
const response = ctx.getResponse<Response>();
|
||||
if (exception instanceof HttpException) {
|
||||
response.status(exception.getStatus()).json(exception.getResponse());
|
||||
} else {
|
||||
response.status(500).json({
|
||||
statusCode: 500,
|
||||
error: exception.message,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -21,7 +21,5 @@ export const serverTimingAndCache = (
|
||||
res.setHeader('Server-Timing', serverTimingValue);
|
||||
});
|
||||
|
||||
res.setHeader('Cache-Control', 'max-age=0, private, must-revalidate');
|
||||
|
||||
next();
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user