Introducción A NestJS
Introducción A NestJS
Introducción a NestJS
Servicio de las Tecnologías de la Información y las Comunicaciones - Universidad de Almería
1 de 95 7/6/21 19:01
Introducción a NestJS https://ualmtorres.github.io/SeminarioNestJS/#trueel-ser...
Tabla de contenidos
Resumen
1. Introducción
2. Creación del proyecto
2.1. Funcionamiento (Servicios y Controladores)
2.2. De�nir un pre�jo para la API.
3. Creación de nuestro primer servicio y controlador
3.1. El servicio
3.2. El controlador
4. Creación de la primera versión de los endpoints
4.1. Recuperación de un libro
4.2. Filtrado mediante parámetros. Recuperación de todos los libros en orden descendente.
4.3. Creación de un libro
4.4. Eliminación de un libro
4.5. Modi�cación de un libro
5. Tipado de objetos
5.1. Creación de una interface para libros
5.2. Creación de un DTO para libros
5.3. Modi�cación del controlador para el uso de tipos
5.4. Modi�cación del servicio para el uso de tipos
6. Finalización del mockeado
6.1. El servicio
6.2. El controlador
7. Creación de servicios conectados a bases de datos
7.1. Con�guración de un servidor MySQL
7.2. ORM y el patrón de repositorio
7.3. Con�guración de la conexión a la base de datos
7.4. Creación de entidades
7.5. El servicio
7.6. El controlador
7.7. Módulo para una mejor organización
7.8. Mejora de la con�guración del uso del ORM
7.9. Pruebas de los endpoints con persistencia en la base de datos
7.10. Cambio a un servidor PostgreSQL
8. Autenticación con JSON Web Tokens
8.1. Con�guración de la estrategia Passport
2 de 95 7/6/21 19:01
Introducción a NestJS https://ualmtorres.github.io/SeminarioNestJS/#trueel-ser...
3 de 95 7/6/21 19:01
Introducción a NestJS https://ualmtorres.github.io/SeminarioNestJS/#trueel-ser...
Resumen
NestJS es un framework para el desearrollo de aplicaciones Node.js en el lado del servidor. Se
programa en TypeScript y proporciona una arquitectura en la aplicación que permite el
desarrollo de aplicaciones más fáciles de mantener. Su arquitectura está bastante inspirada en
Angular lo que facilita el trabajo al equipo de desarrollo al no tener que usar dos formas
diferentes de trabajo en el backend y en el frontend.
Objetivos
Usar el CLI de Nest para la creación de componentes de la aplicación
Conocer las diferencias y utilidades de las entities de los ORM (Object Relational Mappers), las
interfaces y los DTO (Data Transfer Objects)
5 de 95 7/6/21 19:01
Introducción a NestJS https://ualmtorres.github.io/SeminarioNestJS/#trueel-ser...
1. Introducción
A la hora de desarrollar un proyecto es importante tener una estructura y una estrategia bien
planeada para la organización del código. En situaciones donde además los requerimientos son
cambiantes es fácil llegar pronto al desastre. Uno de los motivos por los que surge el framework
NestJS (https://nestjs.com/) es precisamente el facilitar que los desarrolladores puedan tener una
estructura modular de código, lo que facilita el desarrollo de aplicaciones NodeJS empresariales.
NestJS es un framework NodeJS construido sobre NodeJS y TypeScript, y que hace uso de Express
(https://expressjs.com/es/). Además ofrece soporte para las principales bases de datos (MySQL,
PostgreSQL, Oracle, SQLite, MongoDB, …), Swagger (OpenAPI) (https://swagger.io/), autenticación,
logging, y una arquitectura inspirada en Angular (https://angular.io/), características que lo hacen
un framework bastante interesante.
6 de 95 7/6/21 19:01
Introducción a NestJS https://ualmtorres.github.io/SeminarioNestJS/#trueel-ser...
await app.listen(3000); 1 TS
7 de 95 7/6/21 19:01
Introducción a NestJS https://ualmtorres.github.io/SeminarioNestJS/#trueel-ser...
CLI de NestJS añade el decorador @Injectable a los servicios durante su creación. Estos
servicios se podrán inyectar en controladores o en otros servicios.
Archivo app.service.ts
@Injectable() 1
export class AppService {
getHello(): string { 2
return 'Hello World!';
}
}
Decorador que permite que el servicio pueda ser inyectado en controladores y en otros
1
servicios
El controlador se encarga por un lado de escuchar las peticiones que llegan a la aplicación. Por
otro lado, se encarga de preparar las respuestas que proporciona la aplicación. El CLI de NestJS
añade el decorador @Controller a los controladores durante su creación. NestJS permite el uso
de rutas como parámetros del decorador @Controller
Archivo app.controller.ts
@Controller() 2
export class AppController {
constructor(private readonly appService: AppService) {} 3
@Get() 4
getHello(): string { 5
return this.appService.getHello(); 6
}
}
8 de 95 7/6/21 19:01
Introducción a NestJS https://ualmtorres.github.io/SeminarioNestJS/#trueel-ser...
1 Prefijo global
BASH
http://localhost:3000/api/v1
JSON
{
"statusCode": 404,
"message": "Cannot GET /",
"error": "Not Found"
}
9 de 95 7/6/21 19:01
Introducción a NestJS https://ualmtorres.github.io/SeminarioNestJS/#trueel-ser...
BASH
$ nest g service books
$ nest g controller books
El archivo app.module.ts
@Module({
imports: [],
controllers: [AppController, BooksController], 1
1 Lista de controladores
2 Lista de providers
10 de 95 7/6/21 19:01
Introducción a NestJS https://ualmtorres.github.io/SeminarioNestJS/#trueel-ser...
Un provider puede ser un servicio, pero también puede ser un repositorio, una
factoría o un helper.
3.1. El servicio
Implementamos las funciones que proporcionan los datos.
Archivo books/book.service.ts
@Injectable()
export class BooksService {
findAll(): any { 1
return 'findAll funcionando';
}
}
1 Ejemplo de función que se limita a indicar que está funcionando cuando es llamada
3.2. El controlador
Comenzamos añadiendo simplemente por ahora:
TS
11 de 95 7/6/21 19:01
Introducción a NestJS https://ualmtorres.github.io/SeminarioNestJS/#trueel-ser...
@Controller('books')
export class BooksController {
constructor(private booksService: BooksService) {} 2
@Get() 3
findAll() { 4
return this.booksService.findAll(); 5
}
}
12 de 95 7/6/21 19:01
Introducción a NestJS https://ualmtorres.github.io/SeminarioNestJS/#trueel-ser...
13 de 95 7/6/21 19:01
Introducción a NestJS https://ualmtorres.github.io/SeminarioNestJS/#trueel-ser...
Table 1. Endpoints
Archivo books/book.service.ts
... TS
findBook(bookId: string) {
return `findBook funcionando con bookId: ${bookId}`;
}
...
4.1.2. El controlador
Añadimos la ruta que implementa la petición. Tomará como parámetro el id del libro ( bookId ).
Usaremos el decorador NestJS @Param para obtener el parámetro de la petición.
14 de 95 7/6/21 19:01
Introducción a NestJS https://ualmtorres.github.io/SeminarioNestJS/#trueel-ser...
Archivo books/book.controller.ts
return this.booksService.findBook(bookId); 3
}
...
@Get(':RequestedBookId') TS
findBook(@Param('RequestedBookId') methodBookId: string) {
return this.booksService.findBook(methodBookId);
}
15 de 95 7/6/21 19:01
Introducción a NestJS https://ualmtorres.github.io/SeminarioNestJS/#trueel-ser...
1. http://localhost:3000/api/v1/books/sort/1
2. http://localhost:3000/api/v1/books?sort=1
Para resolver la duda nos debemos plantear si la estructura de los datos devueltos cambia
de un caso a otro o es la misma en los dos casos. Si cambia estaríamos ante un nuevo
16 de 95 7/6/21 19:01
Introducción a NestJS https://ualmtorres.github.io/SeminarioNestJS/#trueel-ser...
En este caso, la ordenación sigue presentando los datos siguiendo la misma estructura. Es
decir, sigue siendo una lista de libros igualmente. Lo único es que se presenta ordenada. El
servicio tendrá que capturar los parámetros y devolver los datos de acuerdo a la petición
realizada.
Esta misma solución es aplicable si hay varios parámetros. Por ejemplo, ordenación,
limitación de cantidad de resultados, offsets, filtrado por algún campo, etc. En todos estos
casos se sigue devolviendo una lista de resultados con la misma estructura (p.e. libros).
tratar y permite que los parámetros sean opcionales. El servicio tendrá que
encargarse de determinar cómo trabajar con los parámetros de la petición.
Como la petición de recuperación de libros de forma ordenada sigue devolviendo una lista
de libros con la misma estructura, optamos por implementar esta funcionalidad mediante
parámetros, trasladando la lógica de su interptretación al servicio.
4.2.1. El servicio
La versión preliminar del servicio parametrizado modificará el servicio existente de
recuperación de libros. La función tomará los argumentos y se limitará a devolver un mensaje
con el propio nombre de la función y el argumento (si existe). Esto permite comprobar que la
función ha sido llamada correctamente.
Archivo books/book.service.ts
... TS
findAll(params): any {
return params.length > 0
? `findAll funcionando con ${params}`
: 'findAll funcionando';
}
...
4.2.2. El controlador
Modificamos la ruta que implementa la petición. Tomará como parámetro el tipo de ordenación.
Usaremos el decorador NestJS @Query para obtener el parámetro de la petición.
17 de 95 7/6/21 19:01
Introducción a NestJS https://ualmtorres.github.io/SeminarioNestJS/#trueel-ser...
Archivo books/book.controller.ts
return this.booksService.findAll(params); 4
}
...
El decorador @Req nos permite acceder a todos los datos de una petición. En nuestro caso
estamos interesados en acceder a query . Esta query contiene un JSON con los pares parámetro-
valor pasados en la petición. La idea es pasar directamente este JSON al servicio y que sea el
servicio en que se encargue de acceder a su contenido y actuar como corresponda.
TS
18 de 95 7/6/21 19:01
Introducción a NestJS https://ualmtorres.github.io/SeminarioNestJS/#trueel-ser...
...
findAll(params): any {
let msg = `findAll funcionando. Parámetros:`;
return msg;
}
...
@Controller('books')
export class BooksController {
constructor(private booksService: BooksService) {}
@Get()
findAll(@Req() request: Request) { 1
return this.booksService.findAll(request.query); 2
}
...
}
2 Llamada al servicio con el JSON con los pares clave-valor de los parámetros de la petición
La pantalla siguiente muestra el resultado de realizar la petición con dos parámetros order y
limit .
19 de 95 7/6/21 19:01
Introducción a NestJS https://ualmtorres.github.io/SeminarioNestJS/#trueel-ser...
JSON
{
"title": "El enigma de la habitación 622",
"genre": "Ficción contemporánea",
"description": "Vuelve el «principito de la literatura negra contemporánea, el niño
mimado de la industria literaria» (GQ): el nuevo thriller de Joël Dicker es su novela más
personal. ",
"author": "Joël Dicker",
"publisher": "Alfaguara",
"pages": 624,
"image_url": "https://images-na.ssl-images-amazon.com/images
/I/41KiZbwOhhL._SX315_BO1,204,203,200_.jpg"
}
4.3.1. El servicio
La versión preliminar del servicio para crear un nuevo libro se limitará a devolver el libro que le
20 de 95 7/6/21 19:01
Introducción a NestJS https://ualmtorres.github.io/SeminarioNestJS/#trueel-ser...
llega como parámetro. Esto permite comprobar que la función ha sido llamada correctamente.
Archivo books/book.service.ts
... TS
createBook(newBook: any) {
return newBook;
}
...
4.3.2. El controlador
El decorador @Body nos permite acceder al body enviado en una petición.
Archivo books/book.controller.ts
import { TS
Post,
Body,
} from '@nestjs/common';
import { BooksService } from './books.service';
...
@Controller('books')
export class BooksController {
constructor(private booksService: BooksService) {}
...
@Post() 1
createBook(@Body() body) { 2
let newBook: any = body; 3
return this.booksService.createBook(newBook); 4
}
}
Decorador para el objeto body . Los datos pasados para el nuevo libro se tratan en la
2
variable body
La figura siguiente muestra el resultado de la operación POST con el nuevo libro y la respuesta
obtenida.
21 de 95 7/6/21 19:01
Introducción a NestJS https://ualmtorres.github.io/SeminarioNestJS/#trueel-ser...
4.4.1. El servicio
Añadimos la función que implementa el servicio de eliminación de un libro. Se trata de una
función muy similar a la de buscar un libro. Tomará como argumento el id del libro e
inicialmente se limitará a devolver un mensaje con el nombre de la función y el id pasado como
argumento. Esto permite comprobar que la función ha sido llamada correctamente.
Archivo books/book.service.ts
TS
22 de 95 7/6/21 19:01
Introducción a NestJS https://ualmtorres.github.io/SeminarioNestJS/#trueel-ser...
...
deleteBook(bookId: string) {
return `deleteBook funcionando con bookId: ${bookId}`;
}
...
4.4.2. El controlador
Añadimos la ruta que implementa la petición. Tomará como parámetro el id del libro ( bookId ).
Usaremos el decorador NestJS @Delete
Archivo books/book.controller.ts
... TS
@Controller('books')
export class BooksController {
...
@Delete(':bookId') 1
deleteBook(@Param('bookId') bookId: string) { 2
return this.booksService.deleteBook(bookId); 3
}
...
23 de 95 7/6/21 19:01
Introducción a NestJS https://ualmtorres.github.io/SeminarioNestJS/#trueel-ser...
4.5.1. El servicio
Añadimos la función que implementa el servicio de modificación de un libro. Tomará como
argumentos el id del libro y los nuevos datos del libro. Inicialmente devolverá los datos del libro
modificado. Esto permite comprobar que la función ha sido llamada correctamente.
Archivo books/book.service.ts
... TS
updateBook(bookId: string, newBook: any) {
return newBook;
}
...
4.5.2. El controlador
Añadimos la ruta que implementa la petición. Tomará como parámetro el id del libro ( bookId ).
Usaremos el decorador NestJS @Put
Archivo books/book.controller.ts
24 de 95 7/6/21 19:01
Introducción a NestJS https://ualmtorres.github.io/SeminarioNestJS/#trueel-ser...
... TS
@Controller('books')
export class BooksController {
...
@Put(':bookId') 1
updateBook(@Param('bookId') bookId: string, @Body() body) { 2
25 de 95 7/6/21 19:01
Introducción a NestJS https://ualmtorres.github.io/SeminarioNestJS/#trueel-ser...
26 de 95 7/6/21 19:01
Introducción a NestJS https://ualmtorres.github.io/SeminarioNestJS/#trueel-ser...
5. Tipado de objetos
Hasta ahora hemos tratados con el objeto libro, con el body de las peticiones que hacen POST o
PUT y en ninguna hemos indicado un tipo de datos. Su tipo queda entonces como any . Sin
embargo, esto no es una buena práctica. El uso de tipos nos permitirá durante el desarrollo
determinar las propiedades aplicables a un objeto, la estructura que tienen que tener los objetos
de las peticiones, y demás.
En este tutorial vamos a ver distintos tipos aplicables a los objetos. Para favorecer su
comprensión seguimos con el ejemplo de los libros y suponemos que vamos a usar una base de
datos para persistir los datos. En este caso tendríamos lo siguiente:
En la capa de base de datos los libros se podría modelar como una tabla en una base de datos
relacional, como una colección en una base de datos de documentos,
Los DTO (Data Transfer Objects). Por último, hemos visto que las peticiones envían sus datos
para que sean procesados por los servicios. Sin embargo, los datos enviados en las peticiones
no tienen por que tener la misma estructura que las interfaces o que las entities definidas. Por
ejemplo, en la petición para crear un libro puede que no se envíe el id del libro a crear
porque se trata de un valor generado por el sistema. Por tanto, el tipo usado en la petición
podría no coincidir con alguno de los tipos anteriores (entities, DTO). Estaríamos hablando de
un tipo exclusivo para la creación de libros (el tipo que contiene las propiedades que se pasan
para crear un libro). Además, operaciones diferentes podrían usar tipos diferentes. Un caso
sería que las modificaciones no permitiesen modificar todos los campos de un libro.
Estaríamos ante un nuevo tipo, el tipo de los objetos a modificar. A este tipo de objetos se les
denomina DTO. (Es habitual usar CreateBookDTO , UpdateBookDTO para representar los tipos
27 de 95 7/6/21 19:01
Introducción a NestJS https://ualmtorres.github.io/SeminarioNestJS/#trueel-ser...
de los datos pasados al crear y actualizar libros si los tipos son diferentes)
El DTO de los libros no contiene el id del libro. Esto se debe a que es una
propiedad que los usuarios no envían en sus peticiones.
28 de 95 7/6/21 19:01
Introducción a NestJS https://ualmtorres.github.io/SeminarioNestJS/#trueel-ser...
... TYPESCRIPT
import { BookDto } from './book.dto'; 1
@Controller('books')
export class BooksController {
...
@Post()
createBook(@Body() newBook: BookDto) { 2
return this.booksService.createBook(newBook); 3
....
@Put(':bookId')
updateBook(@Param('bookId') bookId: string, @Body() newBook: BookDto) { 4
1 DTO de libro
En este ejemplo se observa que se los objetos nuevos y los objetos modificados
Este tipado permite manipular de forma segura las propiedades de los libros ayudando a
detectarse errores derivados de asignación de valores a tipos incorrectos.
29 de 95 7/6/21 19:01
Introducción a NestJS https://ualmtorres.github.io/SeminarioNestJS/#trueel-ser...
Archivo CreateBook.dto.ts
Archivo UpdateBook.dto.ts
TS
30 de 95 7/6/21 19:01
Introducción a NestJS https://ualmtorres.github.io/SeminarioNestJS/#trueel-ser...
...
import { BookDto } from './book.dto'; 1
@Injectable()
export class BooksService {
...
createBook(newBook: BookDto) { 2
return newBook;
}
...
return newBook;
}
}
1 DTO de libro
Este tipado permite manipular de forma segura las propiedades de los libros ayudando a
detectarse errores derivados de asignación de valores a tipos incorrectos.
31 de 95 7/6/21 19:01
Introducción a NestJS https://ualmtorres.github.io/SeminarioNestJS/#trueel-ser...
6.1. El servicio
El archivo books/boo.service.ts
TS
32 de 95 7/6/21 19:01
Introducción a NestJS https://ualmtorres.github.io/SeminarioNestJS/#trueel-ser...
@Injectable()
export class BooksService {
books: Book[] = [ 3
{
id: 1,
title: 'Una historia de España',
genre: 'Historia',
description:
'Un relato ameno, personal, a ratos irónico, pero siempre único, de nuestra
accidentada historia a través de los siglos. Una obra concebida por el autor para, en
palabras suyas, «divertirme, releer y disfrutar; un pretexto para mirar atrás desde los
tiempos remotos hasta el presente, reflexionar un poco sobre ello y contarlo por escrito de
una manera poco ortodoxa.',
author: 'Arturo Pérez-Reverte',
publisher: 'Alfaguara',
pages: 256,
image_url:
'https://images-na.ssl-images-amazon.com/images/I/41%2B-
e981m1L._SX311_BO1,204,203,200_.jpg',
},
{
id: 2,
title: 'Historia de España contada para escépticos',
genre: 'Historia',
description:
'Como escribe el autor, no pretende ser veraz, justa y desapasionada, porque
ninguna historia lo es. No está hecha para halagar a reyes y gobernantes, ni pretende
halagar a los banqueros, ni a la Conferencia Episcopal, ni al colectivo gay.',
author: 'Juan Eslava Galán',
publisher: 'Booket',
pages: 592,
image_url:
'https://images-na.ssl-images-amazon.com/images
/I/51IyZ5Mq8YL._SX326_BO1,204,203,200_.jpg',
},
];
findAll(params): Book[] { 4
return this.books;
}
33 de 95 7/6/21 19:01
Introducción a NestJS https://ualmtorres.github.io/SeminarioNestJS/#trueel-ser...
book.id = 99;
book.author = newBook.author;
book.description = newBook.description;
book.genre = newBook.genre;
book.image_url = newBook.image_url;
book.pages = newBook.pages;
book.publisher = newBook.publisher;
book.title = newBook.title;
return book;
}
El método toma un BookDto como argumento (libro sin id ) y devuelve un Book , que sí
8
contiene el id . Devuelve un libro modificado a modo de ejemplo
6.2. El controlador
Se trata de usar los tipos que usan los parámetros de las funciones en las peticiones y de los tipos
que devuelven.
Archivo books/books.controller.ts
TS
34 de 95 7/6/21 19:01
Introducción a NestJS https://ualmtorres.github.io/SeminarioNestJS/#trueel-ser...
import {
Controller,
Get,
Param,
Req,
Post,
Body,
Delete,
Put,
} from '@nestjs/common';
import { BooksService } from './books.service';
import { Request } from 'express';
import { BookDto } from './book.dto';
import { Book } from './book.class';
35 de 95 7/6/21 19:01
Introducción a NestJS https://ualmtorres.github.io/SeminarioNestJS/#trueel-ser...
BASH
$ docker run --name tutorial_mysql -e MYSQL_ROOT_PASSWORD=secret -p 3306:3306 -d mysql:5.7
1
Tras unos instantes (algo más si la imagen de MySQL 5.7 no está descargada en el equipo) habrá
un contenedor en ejecución con el nombre tutorial_mysql . Iniciaremos una sesión interactiva
para crear una base de datos, a la que denominaremos tutorial
BASH
$ docker exec -it tutorial_mysql bash
root@d0512407a21d:/# mysql -u root -p
Enter password: 1
...
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
mysql>
mysql> create database tutorial; 2
Query OK, 1 row affected (0.00 sec)
36 de 95 7/6/21 19:01
Introducción a NestJS https://ualmtorres.github.io/SeminarioNestJS/#trueel-ser...
Un ORM nos abstrae del acceso a un gestor de bases de datos específico. Esto nos aisla del gestor
de base de datos elegido y hace que podamos cambiar de gestor de bases de datos de forma muy
sencilla. TypeORM (https://typeorm.io/#/) es un ORM para TypeScript y JavaScript que facilita la
interacción con la base de datos. El uso de TypeORM acelera el proceso de desarrollo modelando
entidades en el código y sincronizando estos modelos con la base de datos. Actualmente
TypeORM ofrece soporte para varias bases de datos relacionales, como PostgreSQL, Oracle,
Microsoft SQL Server, SQLite, e incluso para bases de datos NoSQL, como MongoDB.
Resumiendo, el ORM trabaja con objetos de la base de datos y el repositorio trabaja con objetos
del dominio.
BASH
$ npm install --save @nestjs/typeorm typeorm mysql
37 de 95 7/6/21 19:01
Introducción a NestJS https://ualmtorres.github.io/SeminarioNestJS/#trueel-ser...
Archivo ormconfig,json
JSON
{
"type": "mysql",
"host": "localhost",
"port": 3306,
"username": "root",
"password": "secret",
"database": "tutorial",
"entities": ["dist/**/*.entity.js"], 1
"synchronize": true 2
}
... CODE
TypeOrmModule.forRoot(
{
type: 'mysql',
host: 'localhost',
port: 3306,
username: 'root',
password: 'example',
database: 'my_nestjs_project',
entities: ['dist/**/*.entity.js'],
synchronize: true,
}
...
El problema de este enfoque está en que las credenciales se adjuntarán en los commits que
se hagan de este archivo. En cambio, si almacenamos las credenciales en un archivo
ormconfig.json y lo incluimos en el archivo .gitignore , los datos sensibles
almacenados en ormconfig.json no serán expuestos al hacer commit.
38 de 95 7/6/21 19:01
Introducción a NestJS https://ualmtorres.github.io/SeminarioNestJS/#trueel-ser...
A continuación, para nuestro ejemplo de libros se muestra la definición de una entidad Book con
las columnas siguientes:
id
title
genre
description
author
publisher
pages
image_url
Archivo books/book.entity.ts
TS
39 de 95 7/6/21 19:01
Introducción a NestJS https://ualmtorres.github.io/SeminarioNestJS/#trueel-ser...
@Entity()
export class Book {
@PrimaryGeneratedColumn() 1
id: number;
@Column()
title: string;
@Column()
genre: string;
@Column('text') 2
description: string;
@Column()
author: string;
@Column()
publisher: string;
@Column()
pages: number;
@Column()
image_url: string;
}
7.5. El servicio
El servicio implementa las funciones habituales para operaciones CRUD (find, findOne, create,
delete y update). Se usa el patrón repositorio para trabajar directamente sobre objetos del
dominio (libros en nuestro caso) y olvidarnos de los detalles de la persistencia. Como todas las
funciones interactúan con bases de datos, todas se programan de forma asíncrona y devuelven
una promesa, por lo que habrá que llamarlas con await .
40 de 95 7/6/21 19:01
Introducción a NestJS https://ualmtorres.github.io/SeminarioNestJS/#trueel-ser...
el trabajo con la programación asíncrona surge la pareja async/await . Con esta pareja:
Las funciones son definidas con async para indicar que devuelven una promesa.
Con await indicamos a JavaScript que espere hasta que la promesa se cumpla y
devuelva su resultado.
Archivo books/books.service.ts
TS
41 de 95 7/6/21 19:01
Introducción a NestJS https://ualmtorres.github.io/SeminarioNestJS/#trueel-ser...
@Injectable()
export class BooksService {
constructor(
@InjectRepository(Book) private booksRepository: Repository<Book>, 5
) {}
return this.booksRepository.save(updated); 12
}
}
Estructura de un libro para insertar (tiene todo menos el id , que se genera en la base de
1
datos)
4 Repositorio de TypeORM
42 de 95 7/6/21 19:01
Introducción a NestJS https://ualmtorres.github.io/SeminarioNestJS/#trueel-ser...
Las funciones del servicio se basan en funciones asíncronas del repositorio, que
devuelven promesas y tendrán que ser llamadas con await . Por tanto, las funciones del
6
servicio son async y devuelven promesas personalizadas al tipo con el que trabajan
(libros, arrays de libros, …)
La llamada a los métodos del repositorio devuelven promesas, por lo que llamaremos con
7
await para esperar a que se resuelvan
Asignación de todas las propiedades del libro nuevo al libro antiguo, excepto el id , que
11
no está incluida en el libro nuevo
7.6. El controlador
Básicamente, el controlador es el mismo que teníamos para el mockup salvo que ahora devuelve
promesas, ya que las funciones del servicio ahora devuelven promesas. Además, se cambia el tipo
del objeto libro. Dejamos de usar la interface para pasar a usar la entity del ORM.
Archivo books/books.controller.ts
TS
43 de 95 7/6/21 19:01
Introducción a NestJS https://ualmtorres.github.io/SeminarioNestJS/#trueel-ser...
import {
Controller,
Get,
Param,
Req,
Post,
Body,
Delete,
Put,
} from '@nestjs/common';
import { BooksService } from './books.service';
import { Request } from 'express';
import { BookDto } from './book.dto';
import { Book } from './book.entity'; 1
@Controller('books')
export class BooksController {
@Get()
findAll(@Req() request: Request): Promise<Book[]> { 2
console.log(request.query);
return this.booksService.findAll(request.query);
}
@Get(':bookId')
findBook(@Param('bookId') bookId: string): Promise<Book> {
return this.booksService.findBook(bookId);
}
@Post()
createBook(@Body() newBook: BookDto): Promise<Book> { 3
return this.booksService.createBook(newBook);
}
@Delete(':bookId')
deleteBook(@Param('bookId') bookId: string): Promise<Book> {
return this.booksService.deleteBook(bookId);
}
@Put(':bookId')
updateBook(
@Param('bookId') bookId: string,
@Body() newBook: BookDto, 4
): Promise<Book> {
return this.booksService.updateBook(bookId, newBook);
}
}
44 de 95 7/6/21 19:01
Introducción a NestJS https://ualmtorres.github.io/SeminarioNestJS/#trueel-ser...
3 Cambiamos el tipo any del body por el tipo del DTO del libro a crear
4 Cambiamos el tipo any del body por el tipo del DTO del libro actualizado
Archivo books/books.module.ts
@Module({
imports: [TypeOrmModule.forFeature([Book])], 1
providers: [BooksService], 2
controllers: [BooksController], 3
})
export class BooksModule {}
2 El servicio
3 El controlador
Este archivo ya está preparado para ser colocado en el array imports de app.module.ts .
45 de 95 7/6/21 19:01
Introducción a NestJS https://ualmtorres.github.io/SeminarioNestJS/#trueel-ser...
2. Creación de un servicio de configuración del ORM a partir de los valores de las variables de
entorno.
3. Modificación del archivo app.module.ts para usar la configuración anterior y cargar los
módulos correspondientes (p.e. el de BooksModule creado antes).
TUTORIAL_HOST=localhost ENV
TUTORIAL_PORT=3306
TUTORIAL_USER=root
TUTORIAL_PASSWORD=secret
TUTORIAL_DATABASE=tutorial
Archivo config/config.service.ts
TS
46 de 95 7/6/21 19:01
Introducción a NestJS https://ualmtorres.github.io/SeminarioNestJS/#trueel-ser...
require('dotenv').config();
class ConfigService {
constructor(private env: { [k: string]: string | undefined }) {}
return value;
}
return {
type: 'mysql', 3
host: this.getValue('TUTORIAL_HOST'), 4
port: parseInt(this.getValue('TUTORIAL_PORT')),
username: this.getValue('TUTORIAL_USER'),
password: this.getValue('TUTORIAL_PASSWORD'),
database: this.getValue('TUTORIAL_DATABASE'),
entities: ['dist/**/*.entity.js'], 5
synchronize: true, 6
};
}
}
export { configService };
47 de 95 7/6/21 19:01
Introducción a NestJS https://ualmtorres.github.io/SeminarioNestJS/#trueel-ser...
Archivo app.module.ts
@Module({
imports: [
BooksModule, 1
TypeOrmModule.forRoot( 2
configService.getTypeOrmConfig(),
),
],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
Usaremos Postman para mostrar los resultados de utilizar los distintos endpoints implementados.
48 de 95 7/6/21 19:01
Introducción a NestJS https://ualmtorres.github.io/SeminarioNestJS/#trueel-ser...
Tras insertar todos los libros del Apéndice A. Datos de ejemplo, la figura siguiente muestra el
listado de todos libros. El endpoint usado es /api/v1/books con el método GET .
49 de 95 7/6/21 19:01
Introducción a NestJS https://ualmtorres.github.io/SeminarioNestJS/#trueel-ser...
La figura siguiente muestra los detalles de un libro concreto (el 2). El endpoint usado es /api/v1
/books/2 con el método GET .
50 de 95 7/6/21 19:01
Introducción a NestJS https://ualmtorres.github.io/SeminarioNestJS/#trueel-ser...
La figura siguiente muestra la modificación de un libro. El id del libro a modificar se pasa como
parámetro en la ruta y los datos del libro con sus modificaciones se pasan en el body . Se
devuelve el libro modificado. El ejemplo muestra el cambio del número de páginas del libro 2 al
valor 544. El endpoint usado es /api/v1/books/2 con el método PUT .
51 de 95 7/6/21 19:01
Introducción a NestJS https://ualmtorres.github.io/SeminarioNestJS/#trueel-ser...
La figura siguiente muestra la eliminación de un libro. El id del libro a eliminar se pasa como
parámetro en la ruta. Se devuelve un JSON con los libros eliminados ( affected ). Por ejemplo,
para eliminar el libro con id 3 usaríamos el endpoint /api/v1/books/3 con el método
DELETE .
52 de 95 7/6/21 19:01
Introducción a NestJS https://ualmtorres.github.io/SeminarioNestJS/#trueel-ser...
Si ahora volvemos a consultar todos los libros se verán los cambios en el número de páginas del
libro 2 y que el libro 3 ha sido eliminado.
53 de 95 7/6/21 19:01
Introducción a NestJS https://ualmtorres.github.io/SeminarioNestJS/#trueel-ser...
2. Cambiar las variables de entorno con los nuevos valores de conexión a la base de datos
54 de 95 7/6/21 19:01
Introducción a NestJS https://ualmtorres.github.io/SeminarioNestJS/#trueel-ser...
Archivo start-postgres.sh
SH
#!/bin/bash
set -e
SERVER="tutorial_postgres";
PW="secret";
DB="tutorial";
echo "echo stop & remove old docker [$SERVER] and starting new fresh instance of
[$SERVER]"
(docker kill $SERVER || :) && \
(docker rm $SERVER || :) && \
docker run --name $SERVER -e POSTGRES_PASSWORD=$PW \
-e PGPASSWORD=$PW \
-p 5432:5432 \
-d postgres
# create the db
echo "CREATE DATABASE $DB ENCODING 'UTF-8';" | docker exec -i $SERVER psql -U
postgres
echo "\l" | docker exec -i $SERVER psql -U postgres
BASH
TUTORIAL_HOST=localhost
TUTORIAL_PORT=5432 1
TUTORIAL_USER=postgres 2
TUTORIAL_PASSWORD=secret
TUTORIAL_DATABASE=tutorial
55 de 95 7/6/21 19:01
Introducción a NestJS https://ualmtorres.github.io/SeminarioNestJS/#trueel-ser...
1 Puerto de PostgreSQL
2 Usuario de PostgreSQL
host: this.getValue('TUTORIAL_HOST'),
port: parseInt(this.getValue('TUTORIAL_PORT')),
username: this.getValue('TUTORIAL_USER'),
password: this.getValue('TUTORIAL_PASSWORD'),
database: this.getValue('TUTORIAL_DATABASE'),
entities: ['dist/**/*.entity.js'],
synchronize: true,
};
}
Si ahora pedimos que nos devuelva todos los libros con el endpoint /api/v1/books y un método
GET obtendremos una lista vacía, ya que partimos de una base de datos Postgres vacía.
Tras introducir un nuevo libro y volver a consultar los libros vemos cómo se recuperan los datos
56 de 95 7/6/21 19:01
Introducción a NestJS https://ualmtorres.github.io/SeminarioNestJS/#trueel-ser...
sin problema, confirmándose lo sencillo que es cambiar de gestor de bases de datos si se usa un
ORM.
57 de 95 7/6/21 19:01
Introducción a NestJS https://ualmtorres.github.io/SeminarioNestJS/#trueel-ser...
Una forma sencilla de hacer esto es mediante JWT. En nuestro caso, ya partimos de un servidor
de autorización que genera tokens de acceso a partir de usuario y contraseña. En este tutorial
sólo añadiremos a la aplicación la parte de comprobación de la validez de los tokens y la
restricción del acceso a los endpoints para tokens válidos.
De forma predeterminada, los tokens no están cifrados. La cadena del token es una
serializalización en Base64 que se puede decodificar fácilmente (https://jwt.io/). La cadena del
token está formada por tres partes:
58 de 95 7/6/21 19:01
Introducción a NestJS https://ualmtorres.github.io/SeminarioNestJS/#trueel-ser...
BASH
$ npm install @nestjs/jwt passport passport-jwt @nestjs/passport
Además, necesitaremos una estrategia Passport para la validación del token y configurar la
clave secreta que se usó para firmar el token.
Módulo de autorización para ser importado por los controladores que quieran asegurar sus
59 de 95 7/6/21 19:01
Introducción a NestJS https://ualmtorres.github.io/SeminarioNestJS/#trueel-ser...
endpoints
Archivo utilities/jwt.strategy.ts
@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) { 1
constructor() {
super({
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(), 2
secretOrKey: 'secret', 3
});
}
60 de 95 7/6/21 19:01
Introducción a NestJS https://ualmtorres.github.io/SeminarioNestJS/#trueel-ser...
Archivo utilities/auth.module.ts
Archivo books/books.module.ts
TS
61 de 95 7/6/21 19:01
Introducción a NestJS https://ualmtorres.github.io/SeminarioNestJS/#trueel-ser...
...
import { AuthModule } from '../utilities/auth.module';
@Module({
imports: [
...
, AuthModule], 1
providers: [...],
controllers: [...],
})
Una vez definido el módulo, ya sólo falta proteger los endpoints. Podremos hacerlo de dos
formas:
Archivo books.controller.ts
import { TS
...
UseGuards, 1
} from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport'; 2
...
@Controller('books')
@UseGuards(AuthGuard('jwt')) 3
...
export class BooksController {
...
}
62 de 95 7/6/21 19:01
Introducción a NestJS https://ualmtorres.github.io/SeminarioNestJS/#trueel-ser...
Restricción del acceso a jwt de forma global (a nivel de clase) para todos los endpoints
3
del controlador
Si tratamos de acceder sin token o con un token inválido a cualquier endpoint definido,
obtendremos un mensaje de error 401 Unauthorized , tal y como muestra la figura.
63 de 95 7/6/21 19:01
Introducción a NestJS https://ualmtorres.github.io/SeminarioNestJS/#trueel-ser...
64 de 95 7/6/21 19:01
Introducción a NestJS https://ualmtorres.github.io/SeminarioNestJS/#trueel-ser...
await app.listen(3000);
}
bootstrap();
1 Importaciones necesarias
65 de 95 7/6/21 19:01
Introducción a NestJS https://ualmtorres.github.io/SeminarioNestJS/#trueel-ser...
ejemplo definido siempre que use un DTO o una entidad, lo que facilita bastante
la interacción con la documentación.
Archivo books/book.dto.ts
TS
66 de 95 7/6/21 19:01
Introducción a NestJS https://ualmtorres.github.io/SeminarioNestJS/#trueel-ser...
@ApiProperty({
example: 'Esta edición del Ingenioso hidalgo don Quijote de la Mancha ...',
})
readonly description: string;
1 Importación de decoradores
2 Configuración de propiedades
La anotación Swagger de la entidad es prácticamente igual a la del DTO salvo que también
incluye el id .
Archivo books/book.entity.ts
TS
67 de 95 7/6/21 19:01
Introducción a NestJS https://ualmtorres.github.io/SeminarioNestJS/#trueel-ser...
@Entity()
export class Book {
@ApiProperty({ example: 99 })
@PrimaryGeneratedColumn()
id: number;
@ApiProperty({
example: 'Esta edición del Ingenioso hidalgo don Quijote de la Mancha ...',
})
@Column('text')
description: string;
68 de 95 7/6/21 19:01
Introducción a NestJS https://ualmtorres.github.io/SeminarioNestJS/#trueel-ser...
Un decorador @ApiResponse() por cada respuesta que proporcione la operación (p.e. 200,
403, …)
... TS
import { BookDto } from './book.dto'; 1
import { Book } from './book.entity'; 2
import { 3
ApiOperation,
ApiResponse,
ApiTags,
ApiBearerAuth,
} from '@nestjs/swagger';
...
@ApiTags('book') 4
@Controller('books')
@UseGuards(AuthGuard('jwt')) 5
@ApiBearerAuth('access-token') 6
export class BooksController {
...
/** 7
*
* @returns {Book[]} Devuelve una lista de libros
* @param {Request} request Lista de parámetros para filtrar
*/
@Get()
@ApiOperation({ summary: 'Obtener lista de libros' }) 8
@ApiResponse({ 9
status: 201,
description: 'Lista de libros',
type: Book, 10
})
findAll(@Req() request: Request): Promise<Book[]> {
...
}
...
}
69 de 95 7/6/21 19:01
Introducción a NestJS https://ualmtorres.github.io/SeminarioNestJS/#trueel-ser...
8 Descripción de la operación
9 Respuesta 201
70 de 95 7/6/21 19:01
Introducción a NestJS https://ualmtorres.github.io/SeminarioNestJS/#trueel-ser...
Si probásemos un endpoint (p.e. GET /books para obtener la lista de todos los libros) con Try
out se nos rechazaría el acceso, tal y como ilustra la figura siguiente.
71 de 95 7/6/21 19:01
Introducción a NestJS https://ualmtorres.github.io/SeminarioNestJS/#trueel-ser...
72 de 95 7/6/21 19:01
Introducción a NestJS https://ualmtorres.github.io/SeminarioNestJS/#trueel-ser...
73 de 95 7/6/21 19:01
Introducción a NestJS https://ualmtorres.github.io/SeminarioNestJS/#trueel-ser...
Si ahora volvemos a probar el endpoint para obtener la lista de libros, la lista se recuperará y se
mostrará en el propio Swagger.
74 de 95 7/6/21 19:01
Introducción a NestJS https://ualmtorres.github.io/SeminarioNestJS/#trueel-ser...
Esto hace a Swagger una opción muy interesante para los proyectos de APIs ya que no sólo es una
herramienta de documentación, sino que también permite la interacción directa con la API. Con
una buena documentación enriquecida con la descripción de sus parámetros, tipos y ejemplos
tendremos una plataforma extraordinaria para la documentación y uso de APIs.
75 de 95 7/6/21 19:01
Introducción a NestJS https://ualmtorres.github.io/SeminarioNestJS/#trueel-ser...
El elemento servers está sin definir. De cara a subir este JSON a un servidor de
76 de 95 7/6/21 19:01
Introducción a NestJS https://ualmtorres.github.io/SeminarioNestJS/#trueel-ser...
BASH
$ npm install --save [email protected]
NestJS-Redoc se apoya en la configuración realizada con Swagger y añade unas opciones propias
(p.e. logo y título de la página). Al igual que con Swagger, la configuración de Redoc se realiza en
main.ts . Sin embargo, hay que indicar que la documentación ya no la sirve Swagger UI, sino
Redoc. De esto se encarga el método setup de RedocModule tal y como se muestra a
continuación.
Archivo main.ts
TYPESCRIPT
77 de 95 7/6/21 19:01
Introducción a NestJS https://ualmtorres.github.io/SeminarioNestJS/#trueel-ser...
...
import { RedocModule, RedocOptions } from 'nestjs-redoc'; 1
await app.listen(3000);
}
bootstrap();
1 Importaciones de Redoc
78 de 95 7/6/21 19:01
Introducción a NestJS https://ualmtorres.github.io/SeminarioNestJS/#trueel-ser...
Commbox (https://www.commbox.io/api/)
Zuora (https://www.zuora.com/developer/api-reference/)
79 de 95 7/6/21 19:01
Introducción a NestJS https://ualmtorres.github.io/SeminarioNestJS/#trueel-ser...
10. Logging
A medida que las aplicaciones se complican y a medida que se les exige mayor rendimiento se
vuelve más necesario contar un registro de logs que nos ayude a encontrar fallos o problemas de
rendimiento. NestJS incorpora un sistema de logging que permite controlar los mensajes que se
registran en el log y especificar su salida. Sin embargo, Nest recomienda usar otros paquetes de
logging más avanzados y versátiles para sistemas en producción, como Winston
(https://github.com/winstonjs/winston). Entre las características de Winston se encuentran: soporte
para gran cantidad de opciones de almacenamiento, niveles de log y formateo de logs.
Niveles: Los niveles de log indican la gravedad, que van desde una caída del sistema hasta el
aviso de una función marcada como obsoleta. Los niveles de log ayudan a ver rápidamente los
logs que necesitan atención. Para cada nivel se puede configurar la cantidad de datos y de
detalles a registrar.
80 de 95 7/6/21 19:01
Introducción a NestJS https://ualmtorres.github.io/SeminarioNestJS/#trueel-ser...
0: error
1: warn
2: info
3: verbose
4: debug
5: silly
BASH
npm install --save nest-winston winston
Archivo app.module.ts
TS
81 de 95 7/6/21 19:01
Introducción a NestJS https://ualmtorres.github.io/SeminarioNestJS/#trueel-ser...
...
import { WinstonModule } from 'nest-winston'; 1
@Module({
imports: [
...
WinstonModule.forRoot({
level: 'info', 2
format: winston.format.combine( 3
winston.format.timestamp({
format: 'YYYY-MM-DD HH:mm:ss',
}),
winston.format.errors({ stack: true }),
winston.format.splat(),
winston.format.json(),
),
transports: [ 4
new winston.transports.File({
dirname: path.join(__dirname, './../log/debug/'),
filename: 'debug.log',
level: 'debug',
}),
new winston.transports.File({
dirname: path.join(__dirname, './../log/error/'),
filename: 'error.log',
level: 'error',
}),
new winston.transports.File({
dirname: path.join(__dirname, './../log/info/'),
filename: 'info.log',
level: 'info',
}),
new winston.transports.Console({ level: 'debug' }),
],
}),
],
controllers: [...],
providers: [...],
})
export class AppModule {}
Importaciones necesarias de Winston y paths para tratar con las rutas de los archivos de
1
log
82 de 95 7/6/21 19:01
Introducción a NestJS https://ualmtorres.github.io/SeminarioNestJS/#trueel-ser...
import { 1 TS
...
Inject } from '@nestjs/common';
import { WINSTON_MODULE_PROVIDER } from 'nest-winston';
import { Logger } from 'winston';
@Controller()
export class SomeController {
constructor(
...
@Inject(WINSTON_MODULE_PROVIDER) private readonly logger: Logger, 2
) {
...
}
...
}
Para crear una entrada de log se indica el nivel de la entrada de log, y concatenaríamos pares
83 de 95 7/6/21 19:01
Introducción a NestJS https://ualmtorres.github.io/SeminarioNestJS/#trueel-ser...
this.logger.log({ TS
level: 'info',
message: 'Hola',
service: 'Books',
});
JSON
{"level":"info","message":"Hola","service":"Books","timestamp":"2020-08-05 19:14:08"} 1
timestamp puede ser incluido de forma automática si se configura así en las opciones de
1
las entradas de log
user : Usuario que ha realizado la petición. Se obtiene del JWT enviado en la cabecera
La mecánica que usaremos para atender una petición de la API será la siguiente:
84 de 95 7/6/21 19:01
Introducción a NestJS https://ualmtorres.github.io/SeminarioNestJS/#trueel-ser...
Para obtener datos de la petición, como el método HTTP, url, usuario y demás,
incluiremos un parámetro de tipo Request en cada función de la API.
Archivo books/books.controller.ts
JSON
...
@Get()
...
findAll(@Req() request: Request): Promise<Book[]> { 1
let startTime = Date.now(); 2
let data = this.booksService.findAll(request.query); 3
return data; 5
}
...
Incluir un parámetro Request para incluir datos como la url, método HTTP y demás en
1
la entrada de log
3 Llamar al servicio
Función auxiliar
Archivo books/books.controller.ts
JSON
85 de 95 7/6/21 19:01
Introducción a NestJS https://ualmtorres.github.io/SeminarioNestJS/#trueel-ser...
...
writeLog(startTime: any, request: any, statusCode: number) {
let finishTime = Date.now();
let elapsedTime = finishTime - startTime;
this.logger.log({
level: 'info',
message: '',
statusCode: statusCode,
method: request['method'],
url: request['url'],
user: request['user'].username,
duration: elapsedTime,
});
}
...
Tras hacer una petición GET /api/v1/books/1 obtendríamos esta entrada en el archivo de logs
log/info/info.log
JSON
{"level":"info","message":"","statusCode":200,"method":"GET","url":"/api/v1/books
/1","user":"mtorres","duration":8,"timestamp":"2020-08-06 13:01:49"}
En este ejemplo se ha optado por definir una entrada de log con campos
86 de 95 7/6/21 19:01
Introducción a NestJS https://ualmtorres.github.io/SeminarioNestJS/#trueel-ser...
/** TS
* Supported comment
*/
BASH
$ npm i -D @compodoc/compodoc
La documentación se generará desde la línea de comandos mediante npx (una herramienta para
ejecutar paquetes de Node disponible con npm 6 ). Esto generará una carpeta documentation en
el proyecto que se podrá servir con el proyecto o en un portal de ámbito más global donde estén
todas las documentaciones de los proyectos desarrollados por el equipo.
BASH
$ npx compodoc -p tsconfig.json -s --theme material
Compodoc genera una página Overview donde presenta un diagrama con los disntintos
componentes y sus relaciones, algo muy interesante para hacerse una primera idea de la
composición e interacción del software desarrollado.
87 de 95 7/6/21 19:01
Introducción a NestJS https://ualmtorres.github.io/SeminarioNestJS/#trueel-ser...
88 de 95 7/6/21 19:01
Introducción a NestJS https://ualmtorres.github.io/SeminarioNestJS/#trueel-ser...
89 de 95 7/6/21 19:01
Introducción a NestJS https://ualmtorres.github.io/SeminarioNestJS/#trueel-ser...
90 de 95 7/6/21 19:01
Introducción a NestJS https://ualmtorres.github.io/SeminarioNestJS/#trueel-ser...
BASH
$ nest generate resource <nombre-recurso>
En primer lugar nos pedirá el tipo de nivel de transporte que queremos usar. Elegiremos REST
API
BASH
? What transport layer do you use? (Use arrow keys)
❯ REST API
GraphQL (code first)
GraphQL (schema first)
Microservice (non-HTTP)
WebSockets
En segundo lugar aceptaremos la generación de los endpoints básicos para las operaciones CRUD.
BASH
? Would you like to generate CRUD entry points? (Y/n)
En el caso de que hayamos elegido crear los recursos para users se crearán los archivos
siguientes y se actualizará src/app.module.ts para añadir el módulo del recurso creado (p.e.
users.module ).
BASH
91 de 95 7/6/21 19:01
Introducción a NestJS https://ualmtorres.github.io/SeminarioNestJS/#trueel-ser...
@Controller('users')
export class UsersController {
constructor(private readonly usersService: UsersService) {}
@Post()
create(@Body() createUserDto: CreateUserDto) {
return this.usersService.create(createUserDto);
}
@Get()
findAll() {
return this.usersService.findAll();
}
@Get(':id')
findOne(@Param('id') id: string) {
return this.usersService.findOne(+id);
}
@Put(':id')
update(@Param('id') id: string, @Body() updateUserDto: UpdateUserDto) {
return this.usersService.update(+id, updateUserDto);
}
@Delete(':id')
remove(@Param('id') id: string) {
return this.usersService.remove(+id);
}
}
92 de 95 7/6/21 19:01
Introducción a NestJS https://ualmtorres.github.io/SeminarioNestJS/#trueel-ser...
@Injectable()
export class UsersService {
create(createUserDto: CreateUserDto) {
return 'This action adds a new user';
}
findAll() {
return `This action returns all users`;
}
findOne(id: number) {
return `This action returns a #${id} user`;
}
remove(id: number) {
return `This action removes a #${id} user`;
}
}
Voilà!! A partir de los archivos generados ya se pueden adaptar los endpoints del controlador,
crear el código de los servicios y adaptar las entidades y los DTOs al caso de base de datos del
proyecto.
PostgreSQL
MySQL
93 de 95 7/6/21 19:01
Introducción a NestJS https://ualmtorres.github.io/SeminarioNestJS/#trueel-ser...
MariaDB
Oracle Database
SQLite
soportadas excepto para Oracle. Para Oracle se debe tener instalado Oracle
Instant Client en el equipo donde se vaya a ejecutar typeorm-model-generator .
npx es una herramienta que permite ejecutar paquetes de binarios npm . npx
queda instalado en versiones posteriores a npm 5.2 .
A continuación se muestran unos parámetros habituales que utilizaremos al generar los modelos
con typeorm-model-generator
-u : Usuario
-x : Contraseña
-p : (opcional) Puerto
BASH
$ npx typeorm-model-generator -h localhost -d myDatabase -u myUser -x myPassword -e oracle
-o ./reservations -p 1527
Esto generará un archivo de entidad para cada tabla encontrada en la base de datos indicada
incluyendo la definición de cada uno de los campos de la tabla.
94 de 95 7/6/21 19:01
Introducción a NestJS https://ualmtorres.github.io/SeminarioNestJS/#trueel-ser...
2 Definición de cada una de las columnas con sus tipos de datos, restricciones, …
95 de 95 7/6/21 19:01