Skip to content

Commit e83aca1

Browse files
update(@core) increase router performance
1 parent 2a6ca01 commit e83aca1

7 files changed

Lines changed: 108 additions & 46 deletions

File tree

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
## 4.5.3
2+
- **core**: [improvement] increase performance
3+
14
## 4.5.2
25
- **common**: [feature] rename `modules` to `imports` (`@Module()` decorator)
36
- **core**: [feature] exception filters with empty `@Catch()` metadata handle each occurred exception

src/core/router/router-execution-context.ts

Lines changed: 72 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -75,40 +75,22 @@ export class RouterExecutionContext {
7575
type === RouteParamtypes.RESPONSE || type === RouteParamtypes.NEXT,
7676
);
7777
const paramsOptions = this.mergeParamsMetatypes(paramsMetadata, paramtypes);
78+
const httpStatusCode = httpCode
79+
? httpCode
80+
: this.responseController.getStatusByMethod(requestMethod);
81+
82+
const fnCanActivate = this.createGuardsFn(guards, instance, callback);
83+
const fnApplyPipes = this.createPipesFn(pipes, paramsOptions);
84+
const fnHandleResponse = this.createHandleResponseFn(
85+
isResponseHandled,
86+
httpStatusCode,
87+
);
7888

7989
return async (req, res, next) => {
8090
const args = this.createNullArray(argsLength);
81-
const canActivate = await this.guardsConsumer.tryActivate(
82-
guards,
83-
req,
84-
instance,
85-
callback,
86-
);
87-
if (!canActivate) {
88-
throw new HttpException(FORBIDDEN_MESSAGE, HttpStatus.FORBIDDEN);
89-
}
90-
91-
await Promise.all(
92-
paramsOptions.map(async param => {
93-
const {
94-
index,
95-
extractValue,
96-
type,
97-
data,
98-
metatype,
99-
pipes: paramPipes,
100-
} = param;
101-
const value = extractValue(req, res, next);
91+
await fnCanActivate(req);
92+
await fnApplyPipes(args, req, res, next);
10293

103-
args[index] = await this.getParamValue(
104-
value,
105-
{ metatype, type, data },
106-
pipes.concat(
107-
this.pipesContextCreator.createConcreteContext(paramPipes),
108-
),
109-
);
110-
}),
111-
);
11294
const handler = () => callback.apply(instance, args);
11395
const result = await this.interceptorsConsumer.intercept(
11496
interceptors,
@@ -117,9 +99,7 @@ export class RouterExecutionContext {
11799
callback,
118100
handler,
119101
);
120-
return !isResponseHandled
121-
? this.responseController.apply(result, res, requestMethod, httpCode)
122-
: undefined;
102+
fnHandleResponse(result, res);
123103
};
124104
}
125105

@@ -215,4 +195,63 @@ export class RouterExecutionContext {
215195
}
216196
return Promise.resolve(value);
217197
}
198+
199+
public createGuardsFn(
200+
guards: any[],
201+
instance: Controller,
202+
callback: (...args) => any,
203+
) {
204+
const canActivateFn = async req => {
205+
const canActivate = await this.guardsConsumer.tryActivate(
206+
guards,
207+
req,
208+
instance,
209+
callback,
210+
);
211+
if (!canActivate) {
212+
throw new HttpException(FORBIDDEN_MESSAGE, HttpStatus.FORBIDDEN);
213+
}
214+
};
215+
return guards.length ? canActivateFn : async req => undefined;
216+
}
217+
218+
public createPipesFn(
219+
pipes: any[],
220+
paramsOptions: (ParamProperties & { metatype?: any })[],
221+
) {
222+
const pipesFn = async (args, req, res, next) => {
223+
await Promise.all(
224+
paramsOptions.map(async param => {
225+
const {
226+
index,
227+
extractValue,
228+
type,
229+
data,
230+
metatype,
231+
pipes: paramPipes,
232+
} = param;
233+
const value = extractValue(req, res, next);
234+
235+
args[index] = await this.getParamValue(
236+
value,
237+
{ metatype, type, data },
238+
pipes.concat(
239+
this.pipesContextCreator.createConcreteContext(paramPipes),
240+
),
241+
);
242+
}),
243+
);
244+
};
245+
return paramsOptions.length ? pipesFn : async (...args) => undefined;
246+
}
247+
248+
public createHandleResponseFn(
249+
isResponseHandled: boolean,
250+
httpStatusCode: number,
251+
) {
252+
return !isResponseHandled
253+
? (result, res) =>
254+
this.responseController.apply(result, res, httpStatusCode)
255+
: (...args) => undefined;
256+
}
218257
}

src/core/router/router-response-controller.ts

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,14 @@ export class RouterResponseController {
66
public async apply(
77
resultOrDeffered,
88
response,
9-
requestMethod: RequestMethod,
10-
httpCode: number,
9+
httpStatusCode: number,
1110
) {
1211
const result = await this.transformToResult(resultOrDeffered);
13-
const statusCode = httpCode
14-
? httpCode
15-
: this.getStatusByMethod(requestMethod);
16-
const res = response.status(statusCode);
12+
const res = response.status(httpStatusCode);
1713
if (isNil(result)) {
1814
return res.send();
1915
}
20-
return isObject(result) ? res.json(result) : res.send(String(result));
16+
return isObject(result) ? res.json(result) : res.send(result);
2117
}
2218

2319
public async transformToResult(resultOrDeffered) {

src/core/test/router/router-execution-context.spec.ts

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,6 @@ describe('RouterExecutionContext', () => {
137137
instance,
138138
'callback',
139139
);
140-
console.log(metadata);
141140

142141
const expectedMetadata = {
143142
[`${RouteParamtypes.REQUEST}:0`]: {
@@ -314,4 +313,27 @@ describe('RouterExecutionContext', () => {
314313
});
315314
});
316315
});
316+
describe('createPipesFn', () => {
317+
describe('when "paramsOptions" is empty', () => {
318+
it('returns identity(undefined)', async () => {
319+
const pipesFn = contextCreator.createPipesFn([], []);
320+
expect(await pipesFn()).to.be.undefined;
321+
});
322+
});
323+
});
324+
describe('createGuardsFn', () => {
325+
it('should throw exception when "tryActivate" returns false', () => {
326+
const guardsFn = contextCreator.createGuardsFn([null], null, null);
327+
sinon.stub(guardsConsumer, 'tryActivate', () => false);
328+
expect(guardsFn({})).to.eventually.throw();
329+
});
330+
});
331+
describe('createHandleResponseFn', () => {
332+
it('should throw exception when "tryActivate" returns false', () => {
333+
const responseFn = contextCreator.createHandleResponseFn(false, 200);
334+
const controllerApplySpy = sinon.spy((contextCreator['responseController'] as any), 'apply');
335+
responseFn();
336+
expect(controllerApplySpy.calledOnce).to.be.true;
337+
});
338+
});
317339
});

src/core/test/router/router-response-controller.spec.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,22 +27,22 @@ describe('RouterResponseController', () => {
2727
describe('nil', () => {
2828
it('should call send()', async () => {
2929
const value = null;
30-
await routerResponseController.apply(value, response, 1, 200);
30+
await routerResponseController.apply(value, response, 200);
3131
expect(response.send.called).to.be.true;
3232
});
3333
});
3434
describe('string', () => {
3535
it('should call send(value)', async () => {
3636
const value = 'string';
37-
await routerResponseController.apply(value, response, 1, 200);
37+
await routerResponseController.apply(value, response, 200);
3838
expect(response.send.called).to.be.true;
3939
expect(response.send.calledWith(String(value))).to.be.true;
4040
});
4141
});
4242
describe('object', () => {
4343
it('should call json(value)', async () => {
4444
const value = { test: 'test' };
45-
await routerResponseController.apply(value, response, 1, 200);
45+
await routerResponseController.apply(value, response, 200);
4646
expect(response.json.called).to.be.true;
4747
expect(response.json.calledWith(value)).to.be.true;
4848
});

src/microservices/server/server.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { MessageHandlers } from '../interfaces/message-handlers.interface';
33
import { Observable } from 'rxjs/Observable';
44
import { MicroserviceResponse } from '../index';
55
import { Subscription } from 'rxjs/Subscription';
6+
import { isFunction } from '@nestjs/common/utils/shared.utils';
67
import 'rxjs/add/operator/catch';
78
import 'rxjs/add/operator/finally';
89
import 'rxjs/add/observable/empty';
@@ -37,7 +38,7 @@ export abstract class Server {
3738
public transformToObservable(resultOrDeffered) {
3839
if (resultOrDeffered instanceof Promise) {
3940
return Observable.fromPromise(resultOrDeffered);
40-
} else if (!(resultOrDeffered instanceof Observable)) {
41+
} else if (!(resultOrDeffered && isFunction(resultOrDeffered.subscribe))) {
4142
return Observable.of(resultOrDeffered);
4243
}
4344
return resultOrDeffered;

src/websockets/web-sockets-controller.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import { MiddlewaresInjector } from './middlewares-injector';
1717
import { ApplicationConfig } from '@nestjs/core/application-config';
1818
import { WsContextCreator } from './context/ws-context-creator';
1919
import { Observable } from 'rxjs/Observable';
20+
import { isFunction } from '@nestjs/common/utils/shared.utils';
2021
import 'rxjs/add/observable/fromPromise';
2122
import 'rxjs/add/observable/of';
2223
import 'rxjs/add/operator/switchMap';
@@ -155,7 +156,7 @@ export class WebSocketsController {
155156
defferedResult: Promise<any>,
156157
): Promise<Observable<any>> {
157158
const result = await defferedResult;
158-
if (result instanceof Observable) {
159+
if (result && isFunction(result.subscribe)) {
159160
return result;
160161
}
161162
if (result instanceof Promise) {

0 commit comments

Comments
 (0)