Conectar Proyecto NestJS con MongoDB

por - octubre 06, 2020

 Conectar Proyecto NestJS con MongoDB

MongoDB más NestJS

Introducción

Cuando estamos trabajando en nuestras aplicaciones Web, casi siempre es muy necesario tener persistencia de datos, ya sea para el registro de usuarios, los artículos para una tienda, configuraciones generales, etc. En la siguiente publicación estaré explicando como conectar nuestro proyecto hecho con el Framework NestJS hacia una base de datos MongoDB. Para este tutorial tomaré como referencia estos artículos anteriores Validaciones NestJS y Docker MongoDB, para ir armando las conexiones. Igualmente dejo por aquí en código del proyecto NestJS. También usaremos Postman aquí la JSON collection para que lo descarguen y lo puedan usar.

Que necesitaremos

Necesitaremos lo mismo que tenemos en los post anteriores, que comento a continuación:

  • NodeJS en su última versión estable.
  • Docker y docker-compose.
  • Cliente MongoDB, puede ser NoSQLBooster (estaré usando este) o Robo3t.
  • Editor de código que ustedes prefieran. Yo estaré con Visual Studio Code.

Manos a la obra

Con el proyecto anterior o ya descargado, vamos ejecutar las siguientes instrucciones en el terminal de comandos. Cabe destacar que deben estar situados en la raíz del proyecto en el terminal.

    yarn install
    yarn add @nestjs/mongoose mongoose
    yarn add -D @types/mongoose

Para este proyecto como ven, usaremos la librería mongoose que nos permitira crear modelos y las conexión a nuestra base de datos. Luego vamos a configurar nuestra DB para poder conectarnos, así cuando editemos nuestro código no haya problemas de conexión. Lo primero que haremos es modificar el docker-compose.yml, de la práctica anterior. Importante: si ya tienen el motor de base de datos configurado aparte, pueden saltarse este único paso.

    version: "3.7"

    services:
        mongo:
            image: mongo:4.4
            restart: always
            env_file: .env
            ports: 
                - 27017:27017
            volumes: 
                - bloggerdb:/data/db

    volumes: 
        bloggerdb:

La modificación que agregamos fue la adicionar el volumen para que la DB persista en nuestra PC. Ahora nos conectamos a nuestra DB y crearemos la Base de datos y el usuario que tendrá de lectura/escritura. Dejo las capturas para que le puedan hacer seguimiento.

Crear base de datos

Creación de base de datos.

Nombre de Base de datos

"devnote" porque la configuración del proyecto tenemos este nombre de base de datos.

Crear usuario de Lectura y Escritura

Agregar usuario de lectura y escritura (Read Write).

Configurar credenciales de Usuario

Modificar credenciales para que quede de esta manera. No olvidar darle "run" para que se ejecute está instrucción y cree a dicho usuario.

Ahora sí al código de NestJS, vamos a crearnos el archivo course.schema.ts en el directorio src/course y le vamos a colocar el siguiente código:

    import { Prop, Schema, SchemaFactory, SchemaOptions } from '@nestjs/mongoose';
    import { Document } from 'mongoose';
    import { State } from 'src/app.service';

    @Schema({
        collection: 'courses',
    })
    class CourseModel extends Document {
        @Prop({
            type: String,
            required: true,
        })
        name: string;

        @Prop({
            type: String,
            required: true,
            enum: [ 'completed', 'progress', 'wait' ]
        })
        state: State;
    }

    const CourseSchema = SchemaFactory.createForClass(CourseModel);

    export {
        CourseModel, 
        CourseSchema
    }

Aquí básicamente indicamos que la colección a usar es courses y tendrá dos propiedades name y state, donde los dos son requeridos y para state tiene solo 3 valores posibles con la propiedad de mongoose enum. Nota: Como usamos Typescript, podemos agregar la implementación de la interface ICourse, agréguenlo si ustedes desean.

En lo siguiente es editar el archivo app.module.ts, debe quedar muy parecido a lo siguiente:

    import { Module } from '@nestjs/common';
    import { AppController } from './app.controller';
    import { AppService } from './app.service';
    import { ConfigModule } from '@nestjs/config';
    import { MongooseModule } from '@nestjs/mongoose';
    import { CourseModel, CourseSchema } from './course/course-schema';

    function getConectionMongo(): string {
        return `mongodb://${process.env.DB_USER}:${process.env.DB_PASSWORD}@localhost/${process.env.DB_DATABASE}`;
    }

    @Module({
        imports: [
            ConfigModule.forRoot({
                envFilePath: process.env.NODE_ENV
                    ? `.${process.env.NODE_ENV}.env`
                    : '.development.env',
            }),
            MongooseModule.forRoot(getConectionMongo(), {
                useNewUrlParser: true,
            }),
            MongooseModule.forFeature([{ name: CourseModel.name, schema: CourseSchema }]),
        ],
        controllers: [AppController],
        providers: [AppService],
    })
    export class AppModule {}

Aquí aprovechamos la configuración del process.env para obtener las credenciales y nombre de la base de datos a conectar, igualmente la uri que coloco aquí es precisamente como lo menciona la documentación. Y luego también importamos el modelo a nuestro modulo, cabe destacar que si siguiéramos el estándar de NestJS, deberíamos haber creado un modulo de courses, cuestión que haré en la siguiente publicación. Ya por último les dejo como quedó el código de app.service.ts que es donde haremos el uso de nuestro modelo CourseModel.

    import { Injectable } from '@nestjs/common';
    import { InjectModel } from '@nestjs/mongoose';
    import { Model } from 'mongoose';
    import { CourseModel } from './course/course-schema';

    // Recomendación pasar enum e interfaces a archivos dedicados a tal fin
    export enum State {
        wait,
        progress,
        completed
    }

    export interface ICourseKey {
        // id:number;
        _id:string;
    }

    export interface ICourseAttributes {
        name: string;
        state: State;
    }

    export interface ICourse extends ICourseKey, ICourseAttributes {}

    @Injectable()
    export class AppService {
    
        constructor(@InjectModel(CourseModel.name) private courseModel: Model<CourseModel>) {}

        // private courses: ICourse[] = [];

        async getCourses(): Promise<ICourse[]> {
            // return this.courses;
            return this.courseModel.find({});
        }

        async findCourse(id: number): Promise<ICourse> {
            // return this.courses.find(item => item.id == <number>id);
            return {
                _id: 'mientras',
                name: 'Curse',
                state: State.progress,
            };
        }

        async createCourse(course: ICourseAttributes): Promise<ICourse> {
            /*
                const idNext = (this.courses.length)? this.courses[this.courses.length - 1].id + 1: 1
                const newLength = this.courses.push({ id:idNext, ...course })
                return this.courses[newLength - 1]
            */
            const createdCourse = new this.courseModel(course);
            return createdCourse.save();
        }

        async updateCourse(id: number, course: ICourseAttributes): Promise<ICourse> {
            /*
                const updateIndex: number = this.courses.findIndex(
                    item => item.id == <number>id,
                );
                this.courses[updateIndex].name = course.name;
                this.courses[updateIndex].state = course.state;

                return this.courses[updateIndex];
            */
            return {
                _id: 'mientras',
                name: 'Curse',
                state: State.progress
            }
        }

        async deleteCourse(id: number): Promise<ICourse> {
            /*
                const startIndex: number = this.courses.findIndex(item => item.id === id);
                const deleteCourse = this.courses.splice(startIndex, 1);

                return deleteCourse[0];
            */
            return {
                _id: 'mientras',
                name: 'Curse',
                state: State.progress,
            };
        }
}

Donde ya dejo funcionando createCourse y getCourses,  para que ustedes puedan completar los demás. Ya un poco detallando que se hizo, el ICourseKey ya no tiene un parámetro number sino de tipo string y el atributo ahora es _id, más que todo porque MongoDB funciona este atributo como llave primaria. Se hizo la inyección de CourseModel para la interacción con está colección a través del modelo. También se removió el atributo privado courses que era donde se alojaba nuestra base de datos temporal. Ahora les muestro los resultados de los métodos que ya están listos en el Postman y en base de datos.

Creación del curso desde Postman.

Documento creado

Documento creado con el Post. No olvidar refrescar la DB para ver los cambios.

Listar courses

Obtener listado de cursos.

Conclusión

Aquí damos por terminado este tutorial, ya que les queda bastante práctica para resolver los otros métodos, además que ya aprendimos a conectarnos a nuestra Base de datos de MongoDB. En una próxima publicación si mostraremos como se debe distribuir los módulos para que no tengamos todo sobre app module y tomando en cuenta las buenas prácticas que nos menciona el framework NestJS. No siendo más, me despido, espero sus comentarios hasta pronto.

También te puede interesar

0 comentarios

ToTop