Let's create our first NestJS application with the CLI.
nest new acme-api
You can also use shorthand syntax.
nest n acme-api
This command will initialize a new Nest project. It will prompt you to ask which package manager you prefer.
? Which package manager would you ❤️ to use? (Use arrow keys)
npm
❯ yarn
Let's choose Yarn
. Let's grab a cup of coffee while the CLI generates the project.
▹▸▹▹▹ Installation in progress... ☕
After a few moments, your new project will be ready.
Thanks for installing Nest 🙏
Please consider donating to our open collective
to help us maintain this package.
🍷 Donate: https://opencollective.com/nest
Let's explore the structure of our NestJS application. The CLI created a project directory called acme-api
. Open up the acme-api
folder in VSCode.
cd acme-api
code .
The required dependencies (node_modules
) are already installed and a few boilerplate files were scaffolded as well. The src/
directory contains several core files necessary for any NestJS application.
.
├── src
│ ├── app.controller.spec.ts
│ ├── app.controller.ts
│ ├── app.module.ts
│ ├── app.service.ts
│ └── main.ts
├── test
│ ├── app.e2e-spec.ts
│ └── jest-e2e.json
├── .eslintrc.js
├── .gitignore
├── .prettierrc
├── nest-cli.json
├── package.json
├── README.md
├── tsconfig.build.json
├── tsconfig.json
└── yarn.lock
Let's focus on the most important files.
File | Purpose |
---|---|
app.controller.ts |
A basic controller. |
app.controller.spec.ts |
Unit tests for the controller. |
app.module.ts |
The root module to bootstrap the application. |
app.service.ts |
A basic service that can be injected. |
main.ts |
The entry file of the application which uses NestFactory to create a Nest application instance. |
package.json |
The application manifest. |
nest-cli.json |
Configuration for the Nest CLI. |
tsconfig.json & tsconfig.build.json |
TypeScript configuration. Used by the Nest CLI to build the application. |
yarn.lock |
Lock file to ensure consistent installs across machines. (package-lock.json if you chose NPM as the package manager.) |
Via the nest-cli.json
you can configure your project's structure and the behavior of the Nest CLI. The NestCLI also translates any configured paths in tsconfig.json
. If you don't build your application via the Nest CLI you will have to do this translation yourself or use a module such as tsconfig-paths. NestJS also uses tsconfig-paths
but via its CLI it will translate these paths during compilation. This is out of scope for this course, but it is best to be aware of it.
The application's entry file main.ts
contains one async
function, which bootstraps
our application. Let's take a deeper look.
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
await app.listen(3000);
}
bootstrap();
The static create
method of NestFactory
is used to create a NestJS application instance (INestApplication
). We only need to pass in our root application module's class.
Nest aims to be platform-agnostic. NestJS can work with any Node.js
HTTP framework once an adapter is created. Out-of-the-box NestJS supports two HTTP platforms: ExpressJS and Fastify.
By default NestJS uses ExpressJS
, but you can easily change this. Using Fastify
instead of ExpressJS
is only a minute of work.
yarn add @nestjs/platform-fastify
import { NestFactory } from '@nestjs/core';
import {
FastifyAdapter,
NestFastifyApplication,
} from '@nestjs/platform-fastify';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create<NestFastifyApplication>(
AppModule,
new FastifyAdapter()
);
await app.listen(3000);
}
bootstrap();
NestJS's module system is very similar to that of Angular, but there are some differences. Like Angular NestJS uses decorators to provide metadata (reflect-metadata) to organize the application structure, configure the routing for the API endpoints, configure guards, validation...etc.
A module is a class annotated with a @Module
decorator. The application must have at least one root module. The root module is the starting point Nest uses to build the application graph. This graph is the internal data structure that Nest uses to resolve module and provider relationships and dependencies. It is strongly recommended to use modules to organize your application. Encapsulate closely related functionality into a separate module.
Let's take a look at our application's root module.
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
@Module({
imports: [],
controllers: [AppController],
providers: [AppService],
exports: [],
})
export class AppModule {}
The @Module()
decorator has a single required parameter used to describe the module. This object consists out of the following properties
Property | Purpose |
---|---|
imports |
List of imported modules. The exported providers of the imported modules are imported. |
controllers |
List of controllers defined in this module. |
providers |
List of providers that are instantiated by the Nest injector and can be shared across this module. |
exports |
Subset of providers provided by this module that is exported and can be imported by other modules. |
By default, a module encapsulates its providers. It is impossible to inject providers that are not part of the current module or are not exported by imported modules. Providers exported by a module are considered as part of that module's public interface/contract/API.
Before we continue, let's remove the following files:
app.controller.ts
app.controller.spec.ts
app.service.ts
Afterward, update the main application module (app.module.ts
).
import { Module } from '@nestjs/common';
@Module({
imports: [],
controllers: [],
providers: [],
})
export class AppModule {}
Let's create a new feature
module that encapsulates the functionality of the car insurance quote API that we are building.
Let's use the CLI to do this.
nest generate module car-insurance-quote
Or just like the Angular CLI, you can also use shorthand notation.
nest g mo car-insurance-quote
You will notice that the CLI created a new directory inside of the src/
folder called car-insurance-quote
. It contains one file car-insurance-quote.module
which declares our new module.
└── src
└── car-insurance-quote
└── car-insurance-quote.module.ts
The new module was also automatically registered in the application's root module and imported.
import { Module } from '@nestjs/common';
import { CarInsuranceQuoteModule } from './car-insurance-quote/car-insurance-quote.module';
@Module({
imports: [CarInsuranceQuoteModule],
controllers: [],
providers: [],
})
export class AppModule {}
This way when the application is booted NestJS will import the root module, inspects its imports, and in turn will import the new CarInsuranceQuoteModule
module and all of its dependencies. We just expanded the application graph.
Time to create our first controller for the car insurance quote module. Let's use the CLI again with the shorthand syntax to create the controller.
nest g co quote car-insurance-quote --flat --no-spec
This adds a controller to our new module and automatically registers it in the @Module()
decorator's controllers
array. The --flat
flag makes sure that the CLI does not create a new folder to contain the controller and the --no-spec
flag specifies that we do not want to create a spec file containing e unit tests for this it (quote.controller.spec.ts
). Unit tests are outside of the scope of this course.
You will have noticed by now that each time when you generate something via the CLI it reports which files were created or updated.
CREATE src/car-insurance-quote/quote.controller.ts (99 bytes)
UPDATE src/car-insurance-quote/car-insurance-quote.module.ts (189 bytes)
You can use the --dry-run
flag to report the changes that the CLI would make without actually changing the file system.
nest g co quote car-insurance-quote --flat --no-spec --dry-run
The newly created controller only contains a few lines of code. Via the @Controller()
decorator you can specify the prefix/path that will be appended to the application root path. In this case, you will be able to call the endpoints of this controller via http://localhost:3000/quote
.
import { Controller } from '@nestjs/common';
@Controller('quote')
export class QuoteController {}
Let's configure a global prefix that is set for every route registered in our application. Let's open up the main.ts
file and set api
as a global prefix.
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.setGlobalPrefix('api');
await app.listen(3000);
}
bootstrap();
Now we can call the endpoints of our new controller via requests to http://localhost:3000/api/quote
. Let's add a method to the controller that we can call to save new car insurance quotes and test if we can send requests to it.
NestJS provides decorators for all standard HTTP methods: @Get()
, @Post()
, @Put
, @Delete()
, @Patch()
, @Options()
and @Head()
. Including one special decorator called @All()
which defines that an endpoint handles all of them. Each of these decorators accepts an optional prefix/path that is appended to the path.
import { Controller, Post } from '@nestjs/common';
@Controller('quote')
export class QuoteController {
@Post('calculate')
public async post(): Promise<any> {
return 'hello, world!';
}
}
We can now save new quotes by sending a POST
request to http://localhost:3000/api/quote/calculate
. Let's test this.
Start the application.
yarn start
And make a cURL
request.
curl --location --request POST 'http://localhost:3000/api/quote/calculate'
Output:
hello, world!%
Using another HTTP verb will result in a 404
status code being returned.
curl --location --request GET 'http://localhost:3000/api/quote/calculate'
{
"statusCode": 404,
"message": "Cannot GET /api/quote/calculate",
"error": "Not Found"
}
The response status code is always 200
by default, except for POST
requests which default to 201
. You can change the returned status code by applying the @HttpCode()
decorator to the controller method.
import { Controller, HttpCode, Post } from '@nestjs/common';
@Controller('quote')
export class QuoteController {
@Post('calculate')
@HttpCode(201)
public async post(): Promise<any> {
return 'hello, world!';
}
}
Of course, if we want to calculate a car insurance quote we need to send some data along with the request and validate it. Let's do this in the next module Pipes and Validations.