forked from kyle-mccarthy/nest-next
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathrender.filter.ts
139 lines (112 loc) · 3.93 KB
/
render.filter.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
import {
ArgumentsHost,
Catch,
ExceptionFilter,
HttpStatus,
} from '@nestjs/common';
import { IncomingMessage, ServerResponse } from 'http';
import { parse as parseUrl } from 'url';
import { RenderService } from './render.service';
import { ErrorResponse } from './types';
@Catch()
export class RenderFilter implements ExceptionFilter {
private readonly service: RenderService;
constructor(service: RenderService) {
this.service = service;
}
/**
* Nest isn't aware of how next handles routing for the build assets, let next
* handle routing for any request that isn't handled by a controller
* @param err
* @param host
*/
public async catch(err: any, host: ArgumentsHost) {
const ctx = host.switchToHttp();
const request = ctx.getRequest();
const response = ctx.getResponse();
if (response && request) {
const requestHandler = this.service.getRequestHandler();
const errorRenderer = this.service.getErrorRenderer();
// these really should already always be set since it is done during the module registration
// if somehow they aren't throw an error
if (!requestHandler || !errorRenderer) {
throw new Error(
'Request and/or error renderer not set on RenderService',
);
}
const isFastify = !!response.res;
const res: ServerResponse = isFastify ? response.res : response;
const req: IncomingMessage = isFastify ? request.raw : request;
if (!res.headersSent && req.url) {
// check to see if the URL requested is an internal nextjs route
// if internal, the url is to some asset (ex /_next/*) that needs to be rendered by nextjs
if (this.service.isInternalUrl(req.url)) {
if (isFastify) {
response.sent = true;
}
return requestHandler(req, res);
}
// let next handle the error
// it's possible that the err doesn't contain a status code, if this is the case treat
// it as an internal server error
res.statusCode = err && err.status ? err.status : 500;
const { pathname, query } = parseUrl(req.url, true);
const errorHandler = this.service.getErrorHandler();
if (errorHandler) {
await errorHandler(err, request, response, pathname, query);
}
if (response.sent === true || res.headersSent) {
return;
}
const serializedErr = this.serializeError(err);
if (isFastify) {
response.sent = true;
}
if (res.statusCode === HttpStatus.NOT_FOUND) {
if (this.service.passthroughNotFoundErrors()) {
return requestHandler(req, res);
}
return errorRenderer(null, req, res, pathname, {
...query,
[Symbol.for('Error')]: serializedErr,
});
}
return errorRenderer(serializedErr, req, res, pathname, query);
}
return;
}
// if the request and/or response are undefined (as with GraphQL) rethrow the error
throw err;
}
/**
* Serialize the error similarly to method used in Next -- parse error as Nest error type
* @param err
*/
public serializeError(err: any): ErrorResponse {
const out: ErrorResponse = {};
if (!err) {
return out;
}
if (err.stack && this.service.isDev()) {
out.stack = err.stack;
}
if (err.response && typeof err.response === 'object') {
const { statusCode, error, message } = err.response;
out.statusCode = statusCode;
out.name = error;
out.message = message;
} else if (err.message && typeof err.message === 'object') {
const { statusCode, error, message } = err.message;
out.statusCode = statusCode;
out.name = error;
out.message = message;
}
if (!out.statusCode && err.status) {
out.statusCode = err.status;
}
if (!out.message && err.message) {
out.message = err.message;
}
return out;
}
}