NestJs backend and Angular NgRx Data

Angular 8 NgRx Data 8 and NestJS

Alain Boudard

--

Today we will explore the way to connect a simple NestJS server with the latest Angular NgRx Data.

In previous articles, we connected both a json-server and a SpringBoot backend with NgRx Data. With the version 8, this library was integrated in the official NgRx, that’s a good point, and ensures compatibility in the future releases.

NestJS server

Many people already talked about this project, I won’t add too much, but the pros that I see in NestJS are :

  • Code architecture based on Angular-like structure, with Modules and TypeScript.
  • Abstraction of NodeJS Express, and a very easier approach.
  • Spring-like decorators, a rich API to build all common features in a web server.
  • GraphQL ready.
  • Microservices, websockets …

We won’t cover the database aspect here, my focus will be on how to build a very simple project, yet clean (I hope) as a starting point. But that’s a good thing, we will be able to expose a CRUD API with no database, but still it will be working.

Angular workspace

As many of you know, we entered the age of workspaces for Angular projects. One of the other names for that would be monorepo. The reasons for that would be multiple :

  • Handle multiple applications with the same version and have the upgrades much much easily than having to do so in each and every app.
  • Start building libraries and share code among our Angular apps ecosystem.

The best tool available to work with clean and automated workspaces is of course Nx from nrwl.io. It provides schematics out of the box to build complex workspaces, and full-stack applications. That’s exactly what we need.

Also, Nx uses great modern tools like Cypress for e2e testing and Jest for unit test, and I love that.

Note : you don’t need Nx to organize your project, it will be simply a little bit faster here.

https://nx.dev/angular/getting-started/nx-and-cli

Angular Universal

It is worth to mention that a similar approach exists by runnning the NestJS backend with Angular Univeral, but we won’t cover that :)

https://github.com/nestjs/ng-universal

The project

You actually have many options to create the project, have an empty project and then add a frontend with Angular and a backend with NestJS. The installer will prompt you anyway for common stacks :

npx create-nx-workspace todosws
See that Nx actually runs ng new with Nx shematics

Choosing the full-stack option will scaffold 3 projects (4 with the e2e one) :

  • Angular application in /apps/todosws
  • NestJS application in /apps/api
  • Shared code api library in /libs/api-interface

Run both backend and frontend :

ng serve todosws // first cli
ng serve api // second cli
Ok, front is calling back and receiving response

Note that the Angular app can call the NestJS server on a different port because it’s using a proxy.conf.json that was built by Nx for you :

// /apps/todosws/proxy.conf.json
{
"/api": {
"target": "http://localhost:3333",
"secure": false
}
}

Now, of course we will want to add a CRUD api to the back, and get rid of direct HTTP calls in the app.component.ts :

// app.component.ts
export class AppComponent {
hello$ = this.http.get<Message>('/api/hello');
constructor(private http: HttpClient) {}
}

Build the server API with NestJS

We will generate a module for our API, it’s not mandatory, but could be seen as a good practice ;) You can use your Angular Console for using / discovering all the nest shematics, I love the work they did ! Your can use ng generate command or the @nestjs/cli (don’t forget to install it globally).

Note : be careful when running nest cli commands, you might need to be in the proper folder or specify the right path.

PS D:\tests\nx\todosws\apps\api> nest g mo Todo app
CREATE /src/app/todo/todo.module.ts (81 bytes)
UPDATE /src/app/app.module.ts (308 bytes)

Then we need a service and a controller.

PS D:\tests\nx\todosws\apps\api> nest g co todo app
CREATE /src/app/todo/todo.controller.spec.ts (479 bytes)
CREATE /src/app/todo/todo.controller.ts (97 bytes)
UPDATE /src/app/todo/todo.module.ts (166 bytes)
PS D:\tests\nx\todosws\apps\api> nest g s todo app
CREATE /src/app/todo/todo.service.spec.ts (446 bytes)
CREATE /src/app/todo/todo.service.ts (88 bytes)
UPDATE /src/app/todo/todo.module.ts (240 bytes)

Nest server is in watch mode, so you can see that the server route is prepared, because the controller is declaring a /todo route :

But if you test the route, it will fail, since there is no decorator for any HTTP method. Lest’s add a route in the controller with mock objects :

export class Todo {
id: number;
title: string;
}
export const todos: Todo[] = [
{ id: 0, title: 'Discover NgrX Data' },
{ id: 1, title: 'Test new IronMan armor' }
];
@Controller('todo')
export class TodoController {
@Get()
findAll(): Observable<Todo[]> {
return of(todos);
}
}

Let’s add the class (yes, NestJs requires a class and not an interface) and the mock to the library, this way we will be able to use it in the Angular app as well. Feel free to organize your lib a little bit better than these ugly exports :)

I added fields in the Todo class : description and active.

Now the controller looks like that :

import { Todo, mockTodos } from '@todosws/api-interface';
@Controller('todo')
export class TodoController {
@Get()
findAll(): Observable<Todo[]> {
return of(mockTodos);
}
}

Now, we will give the service the responsability to handle the data, let’s create all methods we see usefull :

@Injectable()
export class TodoService {
private todos: Todo[] = [];
constructor() {
this.todos = mockTodos;
}
create(todo: Todo) {
this.todos.push(todo);
}
findAll(): Todo[] {
return this.todos;
}
find(id: number): Todo {
return this.todos.find(item => {
return item.id == id;
});
}
remove(id: number): void {
this.todos = this.todos.filter(item => {
return item.id != id;
});
}
update(id: number, todo: Todo): Todo {
const tmpIndex = this.todos.findIndex(obj => obj.id == id);
const updatedObject = { ...this.todos[tmpIndex], ...todo };
this.todos = [
...this.todos.slice(0, tmpIndex),
updatedObject,
...this.todos.slice(tmpIndex + 1)
];
return updatedObject;
}
}

Let’s add the other routes to the controller, that will simply manipulate the Todos via the service, nothing fancy really.

@Controller('todo')
export class TodoController {
constructor(private todoService: TodoService) {}
@Get()
findAll(): Observable<Todo[]> {
return of(this.todoService.findAll());
}
@Get(':id')
findOne(@Param('id') id): Observable<Todo> {
return of(this.todoService.find(id));
}
@Post()
async create(@Body() todo: Todo) {
this.todoService.create(todo);
return todo;
}
@Put(':id')
update(@Param('id') id: number, @Body() updateTodo: Todo): Observable<Todo> {
return of(this.todoService.update(id, updateTodo));
}
@Delete(':id')
async remove(@Param('id') id: number): Promise<void> {
return this.todoService.remove(id);
}
}

Now all the routes should be active and we could test them with whatever way, postman for example. Note that the controllers will be enhanced later with all NestJS tools, like middlewares, exceptions, filters …

Success, the POST request worked on the nestjs server !

This is indeed a very very simple API, but it seems to work, as I said, we won’t cover database tools, maybe some other time, and you might find other articles about that.

Add swagger on NestJS

This is a very simple step as described here. I alter the configuration a little bit, since /api is the base for REST endpoints, I use /swagger for this tool.

// define setBasePath() so swagger UI can run tests against /api
const options = new DocumentBuilder()
.setTitle('Todo API')
.setDescription('The todo API description')
.setVersion('1.0')
.addTag('todo')
.setBasePath('api')
.build();
const document = SwaggerModule.createDocument(app, options);
SwaggerModule.setup('swagger', app, document);

We can test now Swagger UI on http://localhost:3333/swagger/#/

Swagger UI for testing REST endpoints

Add Logger Middleware

By default, NestJS doesn’t come with a logger, but it’s easy to implement, following this doc. You can track precisely any route or all routes, and check how the server is called.

PS D:\tests\nx\todosws\apps\api> nest g mi common/logger app
CREATE /src/app/common/logger.middleware.spec.ts (188 bytes)
CREATE /src/app/common/logger.middleware.ts (198 bytes)
// app.module.ts
export class AppModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
consumer.apply(LoggerMiddleware).forRoutes('todo');
}
}

Then you can alter your logger the way you want :

export class LoggerMiddleware implements NestMiddleware {
use(req: Request, res: Response, next: Function) {
console.log('request : ' + req.method + ' - ' + req.url);
next();
}
}
// logging :
[Nest] 17228 - 2019-07-11 2:51 PM [NestApplication] Nest application successfully started +5ms
Listening at http://localhost:3333/api
request : GET - /
request : GET - /
request : PUT - /3

Install NgRx Data

We will do as “usual” for NgRx, and then add NgRx Data, since it relies on the following libs : store, effects and entity

npm install @ngrx/schematics --save-devnpm install @ngrx/store @ngrx/effects @ngrx/entity @ngrx/data @ngrx/store-devtools --save

Check the steps here for a starter : https://medium.com/@coco.boudard/starting-with-angular-and-ngrx-store-75e92c90d346

We can add @ngrx /data with the command add (it’s already installed, but it will ad an import line in the main module) :

PS D:\tests\nx\todosws> ng add @ngrx/data
Skipping installation: Package already installed
UPDATE package.json (2340 bytes)
UPDATE apps/todosws/src/app/app.module.ts (640 bytes)

We need to update the app.module to have the mandatory tools : @ngrx/effects and @ngrx/store :

StoreModule.forRoot([]),
EffectsModule.forRoot([]),
!environment.production ? StoreDevtoolsModule.instrument() : [],
EntityDataModule.forRoot(entityConfig)

The entityConfig can look as follows, very simple, with a simple modification : the plural name for the entities (later we will add specifics like a filter or other https://ngrx.io/api/data/EntityCollection#filter).

// store/entity-metadata.ts
const entityMetadata: EntityMetadataMap = {
Todo: {}
};
// because the plural of "todo" is not "todos" by default in Nest
const pluralNames = { Todo: 'Todo' };
export const entityConfig = {
entityMetadata,
pluralNames
};
The initial look of the generated Store by NgRx Data

This is it for the configuration, now we need an Angular service that implements NgRx Data, for example, EntityCollection :

export class TodoService extends EntityCollectionServiceBase<Todo> {
constructor(serviceElementsFactory: EntityCollectionServiceElementsFactory) {
super('Todo', serviceElementsFactory);
}
}

In the component, all we need to do it to call the service getAll() method and observe some of its attributes, like count$ :

counter$: Observable<number>;
constructor(private http: HttpClient, private todoService: TodoService) {
this.counter$ = todoService.count$;
}
ngOnInit(): void {
this.todoService.getAll();
}

And display the counter in the template :

<div>
<span [innerText]="counter$ | async"></span>
</div>
NgRx Store is handled by Data library and counter Observable reflects the state.

Finish the screens

The final app will have a few components to list the Todos and make simple operations. Just like the original app I made.

Pretty the application

We will simply add bootstrap 4 and fontawesome with angular-fontawesome, elegant way to load only needed icons.

Added a filter on the entityMetadata

Conclusion

This is just scratching the surface, but with very minimal code, we have a nice and elegant way to consume a NestJS backend through NgRx standards. Of course the next step will be to connect our server to any kind of database, but for our part, there will be basically no change.

Full sources :

Support NestJS

Become a partner: https://opencollective.com/nest/donate

--

--