Node Nx Tutorial - Step 2: Display todos

Nx.dev Tutorial | Node | Step 2: Display Todos

Great! We now have a server application set up to show some data when going to the /api route.

Next, we're going to add a new service, and set up some server side templates.

Creating a todos service

With Nx, we have the ability to scaffold out new code for our application. Let's create a Todos service and populate some todos!

Run nx generate @nrwl/nest:service todo --project todos --directory app to generate our new service

$ nx generate @nrwl/nest:service todo --project todos --directory app
CREATE apps/todos/src/app/todo/todo.service.spec.ts (453 bytes)
CREATE apps/todos/src/app/todo/todo.service.ts (89 bytes)
UPDATE apps/todos/src/app/app.module.ts (318 bytes)

Services are not the only things that the @nrwl/nest plugin can create. Run nx list @nrwl/nest to see other capabilities that the plugin provides.

Open the newly created file in apps/todos/src/app/todo/todo.service.ts and paste the following code:

1import { Injectable } from '@nestjs/common';
2
3export type Todo = {
4  message: string;
5  done: boolean;
6};
7
8const todos: Todo[] = [
9  { message: 'Take out trash', done: false },
10  { message: 'Continue using Nx', done: false },
11];
12
13@Injectable()
14export class TodosService {
15  getTodos(): Todo[] {
16    return todos;
17  }
18}

Usually services should call some kind of data source (like a database or even a file) but for our tutorial, we'll just populate todos manually.

We now have our Todos service ready!

Install template engine

In order to render some views, we'll need to install a template engine:

yarn add hbs

or

npm install --save hbs

Once the installation process is complete, we need to configure the main.ts file with the following code:

1import { Logger } from '@nestjs/common';
2import { NestFactory } from '@nestjs/core';
3import { NestExpressApplication } from '@nestjs/platform-express';
4import { join } from 'path';
5
6import { AppModule } from './app/app.module';
7
8async function bootstrap() {
9  const app = await NestFactory.create<NestExpressApplication>(AppModule);
10
11  app.setBaseViewsDir(join(__dirname, 'assets', 'views'));
12  app.setViewEngine('hbs');
13
14  const port = process.env.PORT || 3333;
15  await app.listen(port, () => {
16    Logger.log('Listening at http://localhost:' + port);
17  });
18}
19
20bootstrap();

We added configuration for setting up the view engine, and removed the globalPrefix option.

Template rendering

Under the assets directory of the todo's project, we'll create a views directory with an index.hbs file inside with the following content:

1<!DOCTYPE html>
2<html>
3  <head>
4    <meta charset="utf-8" />
5    <title>App</title>
6  </head>
7
8  <body>
9    <ul class="todos">
10      {{#each todos}}
11      <li><input type="checkbox" {{#if done}}checked{{/if}} /> {{message}}</li>
12      {{/each}}
13    </ul>
14  </body>
15</html>

Next, we'll update the app.controller.ts file with the following:

1import { Controller, Get, Render } from '@nestjs/common';
2
3import { AppService } from './app.service';
4import { TodosService } from './todos/todos.service';
5
6@Controller()
7export class AppController {
8  constructor(
9    private readonly appService: AppService,
10    private todosService: TodosService
11  ) {}
12
13  @Get('api')
14  getData() {
15    return this.todosService.getTodos();
16  }
17
18  @Get()
19  @Render('index')
20  root() {
21    return {
22      todos: this.getData(),
23    };
24  }
25}

We changed the @Get decorator for the getData function to point to the api route. We also changed this to call the todosService.getTodos() method.
Then we added the root function which renders the index file from our views directory.

The serve process should still be running. If it isn't, restart the process with nx serve todos