Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/zoltan-nz/nestjs-mvc

Tutorial: build a full stack Node.js application with NestJS and TypeScript
https://github.com/zoltan-nz/nestjs-mvc

nestjs nodejs tutorial typescript

Last synced: 3 months ago
JSON representation

Tutorial: build a full stack Node.js application with NestJS and TypeScript

Awesome Lists containing this project

README

        

# Nest.js MVC

## Implementation Log

Prerequisites:

- Node.js environment
- `yarn` package manager

Install Nest CLI globally.

```bash
$ yarn global add @nestjs/cli
```

Scaffold your project. (In this repository the app name is `nestjs-mvc`.)

```bash
$ nest new nestjs-mvc
$ cd nestjs-mvc
```

Optional step: upgrade `package.json` dependencies to the latest.

```bash
$ yarn global add npm-check-updates
$ ncu -u
$ yarn
```

Optional step: update `format` script in `package.json`, so prettier will format all your project files.

```
"format": "prettier --write '**/*.{ts,tsx,js,jsx,json,md,html}'",
```

Additionally you have to create a `./.prettierignore` file in your project root folder with the following content:

```
coverage
dist
package-lock.json
.cache
.idea
.vscode
```

It is a good practice to have also an `.editorconfig` file in your project's root folder with the following content. More about editorconfig: https://editorconfig.org/

```
root = true

[*]
indent_style = space
indent_size = 2
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true

[*.md]
trim_trailing_whitespace = false
```

Now you can test your scaffolded project. Check `package.json`'s `scripts` section for available commands.

```bash
$ yarn build
$ yarn format
etc.
```

Before any git commit, you should run formatter, linting tool with fix, test and check coverage.

```bash
$ yarn format
$ yarn lint --fix
$ yarn test
$ yarn test:e2e
$ yarn test:cov
```

(Don't forget to create a git commit in this stage.)

Run your project in dev mode and open the app in your browser. (The default address is http://localhost:3000)

```bash
$ yarn start:dev
$ open http://localhost:3000
```

You should see the `Hello World!` message.

Let's update our homepage to render a Handlebar template. We partially follow the instructions from this page, please read it for more details: https://docs.nestjs.com/techniques/mvc

```bash
$ yarn add hbs @types/hbs
```

Update `./src/main.ts`

```typescript
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { NestExpressApplication } from '@nestjs/platform-express';
import { join } from 'path';
import hbs = require('hbs');

async function bootstrap() {
const app = await NestFactory.create(AppModule);

app.useStaticAssets(join(__dirname, '..', 'public'));
app.setBaseViewsDir(join(__dirname, '..', 'views'));
app.setViewEngine('hbs');

hbs.registerPartials(join(__dirname, '..', 'views', 'partials'));

await app.listen(3000);
}

bootstrap();
```

Create two new folders in the project root:

```bash
$ mkdir public views
```

Create a custom `stylesheets` folder in `./public` and add `custom.css`. You can place here your custom styles.

```bash
$ mkdir ./public/stylesheets
$ touch ./public/stylesheets/custom.css
```

Create a few handlebar files in `views` folder.

```
$ touch ./views/about.hbs
$ touch ./views/home.hbs
$ touch ./views/layout.hbs
```

Create `./views/partials` subfolder and add a `navbar` partial.

```bash
$ mkdir ./views/partials
$ touch ./views/partials/navbar.hbs
```

Add content to your templates.

`./views/partials/navbar.hbs`

```handlebars

Nestjs MVC


```

`./views/about.hbs` and `./views/home.hbs`

```handlebars

{{title}}


Welcome to {{title}}


```

`./views/layout.hbs`

```handlebars

{{title}}





{{>navbar}}


{{{body}}}


```

Update `./src/app.controller.ts`.

```typescript
import { Controller, Get, Render } from '@nestjs/common';
import { AppService } from './app.service';

@Controller()
export class AppController {
constructor(private readonly appService: AppService) {}

@Get()
@Render('home')
root() {
return { title: 'Home Page' };
}

@Get('/about')
@Render('about')
about() {
return { title: 'About Page' };
}
}
```

You can update `nodemon` configuration files to watch handlebar files and the `views` folder.

`./nodemon.json`

```json
{
"watch": ["dist", "views"],
"ext": "js,hbs",
"exec": "node dist/main"
}
```

`./nodemon-debug.json`

```json
{
"watch": ["src", "views"],
"ext": "ts,hbs",
"ignore": ["src/**/*.spec.ts"],
"exec": "node --inspect-brk -r ts-node/register -r tsconfig-paths/register src/main.ts"
}
```

You can run your application with `yarn start:dev` and refresh your page in the browser. (http://localhost:3000)

Update tests.

`./src/app.controller.spec.ts`

```typescript
import { Test, TestingModule } from '@nestjs/testing';
import { AppController } from './app.controller';
import { AppService } from './app.service';

describe('AppController', () => {
let appController: AppController;

beforeEach(async () => {
const app: TestingModule = await Test.createTestingModule({
controllers: [AppController],
providers: [AppService],
}).compile();

appController = app.get(AppController);
});

it('renders root', () => {
expect(appController.root()).toStrictEqual({ title: 'Home Page' });
});

it('renders /about', () => {
expect(appController.about()).toStrictEqual({ title: 'About Page' });
});
});
```

`./test/app.e2e-spec.ts`

```typescript
import { Test, TestingModule } from '@nestjs/testing';
import * as request from 'supertest';
import { AppModule } from './../src/app.module';
import { join } from 'path';
import hbs = require('hbs');

describe('AppController (e2e)', () => {
let app;

beforeEach(async () => {
const moduleFixture: TestingModule = await Test.createTestingModule({
imports: [AppModule],
}).compile();

app = moduleFixture.createNestApplication();

app.useStaticAssets(join(__dirname, '..', 'public'));
app.setBaseViewsDir(join(__dirname, '..', 'views'));
app.setViewEngine('hbs');

hbs.registerPartials(join(__dirname, '..', 'views', 'partials'));

await app.init();
});

it('/ (GET)', () => {
return request(app.getHttpServer())
.get('/')
.expect(200);
});

it('/about (GET)', () => {
return request(app.getHttpServer())
.get('/about')
.expect(200);
});
});
```