Skip to content

Commit 7adcd51

Browse files
Merge pull request #1956 from nestjs/8.0.0
8.0.0
2 parents 71df040 + b9537e6 commit 7adcd51

File tree

73 files changed

+1771
-422
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

73 files changed

+1771
-422
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ dist/*
55
!/dist/v4
66
!/dist/v5
77
!/dist/v6
8+
!/dist/v7
89

910
/tmp
1011
/out-tsc

content/controllers.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ export class CatsController {
3939

4040
> info **Hint** To create a controller using the CLI, simply execute the `$ nest g controller cats` command.
4141
42-
The `@Get()` HTTP request method decorator before the `findAll()` method tells Nest to create a handler for a specific endpoint for HTTP requests. The endpoint corresponds to the HTTP request method (GET in this case) and the route path. What is the route path? The route path for a handler is determined by concatenating the (optional) prefix declared for the controller, and any path specified in the request decorator. Since we've declared a prefix for every route ( `cats`), and haven't added any path information in the decorator, Nest will map `GET /cats` requests to this handler. As mentioned, the path includes both the optional controller path prefix **and** any path string declared in the request method decorator. For example, a path prefix of `customers` combined with the decorator `@Get('profile')` would produce a route mapping for requests like `GET /customers/profile`.
42+
The `@Get()` HTTP request method decorator before the `findAll()` method tells Nest to create a handler for a specific endpoint for HTTP requests. The endpoint corresponds to the HTTP request method (GET in this case) and the route path. What is the route path? The route path for a handler is determined by concatenating the (optional) prefix declared for the controller, and any path specified in the method's decorator. Since we've declared a prefix for every route ( `cats`), and haven't added any path information in the decorator, Nest will map `GET /cats` requests to this handler. As mentioned, the path includes both the optional controller path prefix **and** any path string declared in the request method decorator. For example, a path prefix of `customers` combined with the decorator `@Get('profile')` would produce a route mapping for requests like `GET /customers/profile`.
4343

4444
In our example above, when a GET request is made to this endpoint, Nest routes the request to our user-defined `findAll()` method. Note that the method name we choose here is completely arbitrary. We obviously must declare a method to bind the route to, but Nest doesn't attach any significance to the method name chosen.
4545

content/exception-filters.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ Out of the box, this action is performed by a built-in **global exception filter
1515
}
1616
```
1717

18+
> info **Hint** The global exception filter partially supports the `http-errors` library. Basically, any thrown exception containing the `statusCode` and `message` property will be properly populated and send back as a response (instead of the default `InternalServerErrorException` for unrecognized exceptions).
19+
1820
#### Throwing standard exceptions
1921

2022
Nest provides a built-in `HttpException` class, exposed from the `@nestjs/common` package. For typical HTTP REST/GraphQL API based applications, it's best practice to send standard HTTP response objects when certain error conditions occur.

content/faq/global-prefix.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,17 @@ To set a prefix for **every route** registered in an HTTP application, use the `
66
const app = await NestFactory.create(AppModule);
77
app.setGlobalPrefix('v1');
88
```
9+
10+
You can exclude routes from the global prefix using the following construction:
11+
12+
```typescript
13+
app.setGlobalPrefix('v1', {
14+
exclude: [{ path: 'health', method: RequestMethod.GET }],
15+
});
16+
```
17+
18+
Alternatively, you can specify route as a string (it will apply to every request method):
19+
20+
```typescript
21+
app.setGlobalPrefix('v1', { exclude: ['cats'] });
22+
```

content/faq/serverless.md

Lines changed: 324 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,324 @@
1+
### Serverless
2+
3+
Serverless computing is a cloud computing execution model in which the cloud provider allocates machine resources on-demand, taking care of the servers on behalf of their customers. When an app is not in use, there are no computing resources allocated to the app. Pricing is based on the actual amount of resources consumed by an application ([source](https://en.wikipedia.org/wiki/Serverless_computing)).
4+
5+
With a **serverless architecture**, you focus purely on the individual functions in your application code. Services such AWS Lambda, Google Cloud Functions, and Microsoft Azure Functions take care of all the physical hardware, virtual machine operating system, and web server software management.
6+
7+
> info **Hint** This chapter does not cover the pros and cons of serverless functions nor dives into the specifics of any cloud providers.
8+
9+
#### Cold start
10+
11+
A cold start is the first time your code has been executed in a while. Depending on a cloud provider you use, it may span several different operations, from downloading the code and bootstrapping the runtime to eventually running your code.
12+
This process adds **significant latency** depending on several factors, the language, the number of packages your application require, etc.
13+
14+
The cold start is important and although there are things which are beyond our control, there's still a lot of things we can do on our side to make it as short as possible.
15+
16+
While you can think of Nest as a fully-fledged framework designed to be used in complex, enterprise applications,
17+
it is also **suitable for much "simpler" applications** (or scripts). For example, with the use of [Standalone applications](/standalone-applications) feature, you can take advantage of Nest's DI system in simple workers, CRON jobs, CLIs, or serverless functions.
18+
19+
#### Benchmarks
20+
21+
To better understand what's the cost of using Nest or other, well-known libraries (like `express`) in the context of serverless functions, let's compare how much time Node runtime needs to run the following scripts:
22+
23+
```typescript
24+
// #1 Express
25+
import * as express from 'express';
26+
27+
async function bootstrap() {
28+
const app = express();
29+
app.get('/', (req, res) => res.send('Hello world!'));
30+
await new Promise<void>((resolve) => app.listen(3000, resolve));
31+
}
32+
bootstrap();
33+
34+
// #2 Nest (with @nestjs/platform-express)
35+
import { NestFactory } from '@nestjs/core';
36+
import { AppModule } from './app.module';
37+
38+
async function bootstrap() {
39+
const app = await NestFactory.create(AppModule, { logger: ['error'] });
40+
await app.listen(3000);
41+
}
42+
bootstrap();
43+
44+
// #3 Nest as a Standalone application (no HTTP server)
45+
import { NestFactory } from '@nestjs/core';
46+
import { AppModule } from './app.module';
47+
import { AppService } from './app.service';
48+
49+
async function bootstrap() {
50+
const app = await NestFactory.createApplicationContext(AppModule, {
51+
logger: ['error'],
52+
});
53+
console.log(app.get(AppService).getHello());
54+
}
55+
bootstrap();
56+
57+
// #4 Raw Node.js script
58+
async function bootstrap() {
59+
console.log('Hello world!');
60+
}
61+
bootstrap();
62+
```
63+
64+
For all these scripts, we used the `tsc` (TypeScript) compiler and so the code remains unbundled (`webpack` isn't used).
65+
66+
| | |
67+
| ------------------------------------ | ----------------- |
68+
| Express | 0.0079s (7.9ms) |
69+
| Nest with `@nestjs/platform-express` | 0.1974s (197.4ms) |
70+
| Nest (standalone application) | 0.1117s (111.7ms) |
71+
| Raw Node.js script | 0.0071s (7.1ms) |
72+
73+
> info **Note** Machine: MacBook Pro Mid 2014, 2,5 GHz Quad-Core Intel Core i7, 16 GB 1600 MHz DDR3, SSD.
74+
75+
Now, let's repeat all benchmarks but this time, using `webpack` (if you have [Nest CLI](/cli/overview) installed, you can run `nest build --webpack`) to bundle our application into a single executable JavaScript file.
76+
However, instead of using the default `webpack` configuration that Nest CLI ships with, we'll make sure to bundle all dependencies (`node_modules`) together, as follows:
77+
78+
```javascript
79+
module.exports = (options, webpack) => {
80+
const lazyImports = [
81+
'@nestjs/microservices/microservices-module',
82+
'@nestjs/websockets/socket-module',
83+
];
84+
85+
return {
86+
...options,
87+
externals: [],
88+
plugins: [
89+
...options.plugins,
90+
new webpack.IgnorePlugin({
91+
checkResource(resource) {
92+
if (lazyImports.includes(resource)) {
93+
try {
94+
require.resolve(resource);
95+
} catch (err) {
96+
return true;
97+
}
98+
}
99+
return false;
100+
},
101+
}),
102+
],
103+
};
104+
};
105+
```
106+
107+
> info **Hint** To instruct Nest CLI to use this configuration, create a new `webpack.config.js` file in the root directory of your project.
108+
109+
With this configuration, we received the following results:
110+
111+
| | |
112+
| ------------------------------------ | ---------------- |
113+
| Express | 0.0068s (6.8ms) |
114+
| Nest with `@nestjs/platform-express` | 0.0815s (81.5ms) |
115+
| Nest (standalone application) | 0.0319s (31.9ms) |
116+
| Raw Node.js script | 0.0066s (6.6ms) |
117+
118+
> info **Note** Machine: MacBook Pro Mid 2014, 2,5 GHz Quad-Core Intel Core i7, 16 GB 1600 MHz DDR3, SSD.
119+
120+
> info **Hint** You could optimize it even further by applying additional code minification & optimization techniques (using `webpack` plugins, etc.).
121+
122+
As you can see, the way you compile (and whether you bundle your code) is crucial and has a significant impact on the overall startup time. With `webpack`, you can get the bootstrap time of a standalone Nest application (starter project with one module, controller, and service) down to ~32ms on average, and down to ~81.5ms for a regular HTTP, express-based NestJS app.
123+
124+
For more complicated Nest applications, for example, with 10 resources (generated through `$ nest g resource` schematic = 10 modules, 10 controllers, 10 services, 20 DTO classes, 50 HTTP endpoints + `AppModule`), the overall startup on MacBook Pro Mid 2014, 2,5 GHz Quad-Core Intel Core i7, 16 GB 1600 MHz DDR3, SSD is approximately 0.1298s (129.8ms). Running a monolithic application as a serverless function typically doesn't make too much sense anyway, so think of this benchmark more as an example of how the bootstrap time may potentially increase as your application grows.
125+
126+
#### Runtime optimizations
127+
128+
Thus far we covered compile-time optimizations. These are unrelated to the way you define providers and load Nest modules in your application, and that plays an essential role as your application gets bigger.
129+
130+
For example, imagine having a database connection defined as an [asynchronous provider](/fundamentals/async-providers). Async providers are designed to delay the application start until one or more asynchronous tasks are completed.
131+
That means, if your serverless function on average requires 2s to connect to the database (on bootstrap), your endpoint will need at least two extra seconds (because it must wait till the connection is established) to send a response back (when it's a cold start and your application wasn't running already).
132+
133+
As you can see, the way you structure your providers is somewhat different in a **serverless environment** where bootstrap time is important.
134+
Another good example is if you use Redis for caching, but only in certain scenarios. Perhaps, in this case, you should not define a Redis connection as an async provider, as it would slow down the bootstrap time, even if it's not required for this specific function invocation.
135+
136+
Also, sometimes you could lazy-load entire modules, using the `LazyModuleLoader` class, as described in [this chapter](/fundamentals/lazy-loading-modules). Caching is a great example here too.
137+
Imagine that your application has, let's say, `CacheModule` which internally connects to Redis and also, exports the `CacheService` to interact with the Redis storage. If you don't need it for all potential function invocations,
138+
you can just load it on-demand, lazily. This way you'll get a faster startup time (when a cold start occurs) for all invocations that don't require caching.
139+
140+
```typescript
141+
if (request.method === RequestMethod[RequestMethod.GET]) {
142+
const { CacheModule } = await import('./cache.module');
143+
const moduleRef = await this.lazyModuleLoader.load(() => CacheModule);
144+
145+
const { CacheService } = await import('./cache.service');
146+
const cacheService = moduleRef.get(CacheService);
147+
148+
return cacheService.get(ENDPOINT_KEY);
149+
}
150+
```
151+
152+
Another great example is a webhook or worker, which depending on some specific conditions (e.g., input arguments), may perform different operations.
153+
In such a case, you could specify a condition inside your route handler that lazily loads an appropriate module for the specific function invocation, and just load every other module lazily.
154+
155+
```typescript
156+
if (workerType === WorkerType.A) {
157+
const { WorkerAModule } = await import('./worker-a.module');
158+
const moduleRef = await this.lazyModuleLoader.load(() => WorkerAModule);
159+
// ...
160+
} else if (workerType === WorkerType.B) {
161+
const { WorkerBModule } = await import('./worker-b.module');
162+
const moduleRef = await this.lazyModuleLoader.load(() => WorkerBModule);
163+
// ...
164+
}
165+
```
166+
167+
#### Example integration
168+
169+
The way your application's entry file (typically `main.ts` file) is supposed to look like **depends on several factors** and so **there's no single template** that just works for every scenario.
170+
For example, the initialization file required to spin up your serverless function varies by cloud providers (AWS, Azure, GCP, etc.).
171+
Also, depending on whether you want to run a typical HTTP application with multiple routes/endpoints or just provide a single route (or execute a specific portion of code),
172+
your application's code will look different (for example, for the endpoint-per-function approach you could use the `NestFactory.createApplicationContext` instead of booting the HTTP server, setting up middleware, etc.).
173+
174+
Just for illustration purposes, we'll integrate Nest (using `@nestjs/platform-express` and so spinning up the whole, fully functional HTTP router)
175+
with the [Serverless](https://www.serverless.com/) framework (in this case, targetting AWS Lambda). As we've mentioned earlier, your code will differ depending on the cloud provider you choose, and many other factors.
176+
177+
First, let's install the required packages:
178+
179+
```bash
180+
$ npm i @vendia/serverless-express aws-lambda
181+
$ npm i @types/aws-lambda serverless-offline
182+
```
183+
184+
> info **Hint** To speed up development cycles, we install the `serverless-offline` plugin which emulates AWS λ and API Gateway.
185+
186+
Once the installation process is complete, let's create the `serverless.yml` file to configure the Serverless framework:
187+
188+
```yaml
189+
service:
190+
name: serverless-example
191+
192+
plugins:
193+
- serverless-offline
194+
195+
provider:
196+
name: aws
197+
runtime: nodejs12.x
198+
199+
functions:
200+
main:
201+
handler: dist/main.handler
202+
events:
203+
- http:
204+
method: ANY
205+
path: /
206+
- http:
207+
method: ANY
208+
path: '{proxy+}'
209+
```
210+
211+
> info **Hint** To learn more about the Serverless framework, visit the [official documentation](https://www.serverless.com/framework/docs/).
212+
213+
With this place, we can now navigate to the `main.ts` file and update our bootstrap code with the required boilerplate:
214+
215+
```typescript
216+
import { NestFactory } from '@nestjs/core';
217+
import serverlessExpress from '@vendia/serverless-express';
218+
import { Callback, Context, Handler } from 'aws-lambda';
219+
import { AppModule } from './app.module';
220+
221+
let server: Handler;
222+
223+
async function bootstrap(): Promise<Handler> {
224+
const app = await NestFactory.create(AppModule);
225+
await app.init();
226+
227+
const expressApp = app.getHttpAdapter().getInstance();
228+
return serverlessExpress({ app: expressApp });
229+
}
230+
231+
export const handler: Handler = async (
232+
event: any,
233+
context: Context,
234+
callback: Callback,
235+
) => {
236+
server = server ?? (await bootstrap());
237+
return server(event, context, callback);
238+
};
239+
```
240+
241+
> info **Hint** For creating multiple serverless functions and sharing common modules between them, we recommend using the [CLI Monorepo mode](/cli/monorepo#monorepo-mode).
242+
243+
> warning **Warning** If you use `@nestjs/swagger` package, there are a few additional steps required to make it work properly in the context of serverless function. Check out this [article](https://javascript.plainenglish.io/serverless-nestjs-document-your-api-with-swagger-and-aws-api-gateway-64a53962e8a2) for more information.
244+
245+
Next, open up the `tsconfig.json` file and make sure to enable the `esModuleInterop` option to make the `@vendia/serverless-express` package load properly.
246+
247+
```json
248+
{
249+
"compilerOptions": {
250+
...
251+
"esModuleInterop": true
252+
}
253+
}
254+
```
255+
256+
Now we can build our application (with `nest build` or `tsc`) and use the `serverless` CLI to start our lambda function locally:
257+
258+
```bash
259+
$ npm run build
260+
$ npx serverless offline
261+
```
262+
263+
Once the application is running, open your browser and navigate to `http://localhost:3000/dev/[ANY_ROUTE]` (where `[ANY_ROUTE]` is any endpoint registered in your application).
264+
265+
In the sections above, we've shown that using `webpack` and bundling your app can have significant impact on the overall bootstrap time.
266+
However, to make it work with our example, there's one additional configuration you must add in your `webpack.config.js` file. Generally,
267+
to make sure our `handler` function will be picked up, we must change the `output.libraryTarget` property to `commonjs2`.
268+
269+
```javascript
270+
return {
271+
...options,
272+
externals: [],
273+
output: {
274+
libraryTarget: 'commonjs2',
275+
},
276+
// ... the rest of the configuration
277+
};
278+
```
279+
280+
With this in place, you can now use `$ nest build --webpack` to compile your function's code (and then `$ npx serverless offline` to test it).
281+
282+
#### Using standalone application feature
283+
284+
Alternatively, if you want to keep your function very lightweight and you don't need any HTTP-related features (routing, but also guards, interceptors, pipes, etc.),
285+
you can just use `NestFactory.createApplicationContext` (as mentioned earlier) instead of running the entire HTTP server (and `express` under the hood), as follows:
286+
287+
```typescript
288+
@@filename(main)
289+
import { HttpStatus } from '@nestjs/common';
290+
import { NestFactory } from '@nestjs/core';
291+
import { Callback, Context, Handler } from 'aws-lambda';
292+
import { AppModule } from './app.module';
293+
import { AppService } from './app.service';
294+
295+
export const handler: Handler = async (
296+
event: any,
297+
context: Context,
298+
callback: Callback,
299+
) => {
300+
const appContext = await NestFactory.createApplicationContext(AppModule);
301+
const appService = appContext.get(AppService);
302+
303+
return {
304+
body: appService.getHello(),
305+
statusCode: HttpStatus.OK,
306+
};
307+
};
308+
```
309+
310+
> info **Hint** Be aware that `NestFactory.createApplicationContext` does not wrap controller methods with enhancers (guard, interceptors, etc.). For this, you must use the `NestFactory.create` method.
311+
312+
You could also pass the `event` object down to, let's say, `EventsService` provider that could process it and return a corresponding value (depending on the input value and your business logic).
313+
314+
```typescript
315+
export const handler: Handler = async (
316+
event: any,
317+
context: Context,
318+
callback: Callback,
319+
) => {
320+
const appContext = await NestFactory.createApplicationContext(AppModule);
321+
const eventsService = appContext.get(EventsService);
322+
return eventsService.process(event);
323+
};
324+
```

content/fundamentals/async-components.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
### Asynchronous providers
22

3-
At times, the application start should be delayed until one or more **asynchronous tasks** are completed. For example, you may not want to start accepting requests until the connection with the database has been established. You can achieve this using _asynchronous providers_.
3+
At times, the application start should be delayed until one or more **asynchronous tasks** are completed. For example, you may not want to start accepting requests until the connection with the database has been established. You can achieve this using asynchronous providers.
44

55
The syntax for this is to use `async/await` with the `useFactory` syntax. The factory returns a `Promise`, and the factory function can `await` asynchronous tasks. Nest will await resolution of the promise before instantiating any class that depends on (injects) such a provider.
66

0 commit comments

Comments
 (0)