Nestjs API

Api rest con NestJS, Docker, Prisma y Postgres

Instalación

> Terminal
$ npm i -g @nestjs/cli
$ nest new mi-api

Contenerizar la aplicación

Crear el archivo Dockerfile en la raíz del proyecto

> Terminal
$ touch Dockerfile

Copiar el siguiente contenido en el archivo.

Dockerfile
FROM node:21-alpine
 
WORKDIR /usr/src/app
 
COPY package*.json ./
 
RUN npm install
 
COPY . .
 
RUN npx prisma generate
 
RUN npm run build

Crear archivo docker-compose.yml en la raíz del proyecto

> Terminal
$ touch docker-compose.yml

Copiar el siguiente contenido en el archivo.

docker-compose.yml
version: '3.8'
services:
  app:
    container_name: backend-services
    image: backend-services
    build:
        context: .
        dockerfile: ./Dockerfile
    command: npm run start:debug
    ports:
        - 3000:3000
    networks:
        - backend-services
    volumes:
        - .:/usr/src/app
        - /usr/src/app/node_modules
    restart: unless-stopped
    depends_on:
        - postgres
 
  postgres:
    container_name: postgres
    image: postgres
    restart: always
    environment:
      - POSTGRES_USER=${POSTGRES_USER}
      - POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
    volumes:
      - postgres:/var/lib/postgresql/data
    ports:
      - '5432:5432'
    networks:
      - backend-services
 
 
volumes:
  postgres:
networks:
  backend-services:
 

Crear archivo .env en la raíz del proyecto

> Terminal
$ touch .env

Copiar el siguiente contenido en el archivo.

.env
SERVER_PORT=3000

POSTGRES_USER=postgres-user
POSTGRES_PASSWORD=postgres-password
POSTGRES_HOST=localhost
POSTGRES_PORT=5432
POSTGRES_DB=postgres-db

PORT=3000
MODE=DEV

DATABASE_URL="postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@${POSTGRES_HOST}:${POSTGRES_PORT}/${POSTGRES_DB}?connect_timeout=300"

Iniciar el proyecto con docker-compose

> Terminal
$ docker-compose up

Preparar la base de datos

Instalar prisma

> Terminal
$ npm install -D prisma

Inicializar prisma

Esto crea el archivo prisma/schema.prisma

> Terminal
$ npx prisma init

Crear el modelo

Agregar al final de prisma/schema.prisma

prisma/schema.prisma
 
model User {
  id        String   @id @default(uuid())
  email     String   @unique
  name      String?
  password  String
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt
}

Migrar la base de datos

> Terminal
$ npx prisma migrate dev --name init

Alimentar la base de datos con datos iniciales

Para eso es necesario crear el archivo prisma/seed.ts

> Terminal
$ touch prisma/seed.ts

Copiar el siguiente contenido en el archivo.

prisma/seed.ts
import { PrismaClient } from '@prisma/client';
 
const prisma = new PrismaClient();
 
async function main() {
  await prisma.user.create({
    data: {
      name: 'Marcelo',
      email: 'chelop@gmail.com',
      password: '123456',
    }
  })
}
 
main()
  .catch(e => {
    throw e;
  })
  .finally(async () => {
    await prisma.$disconnect();
  });

Luego es necesario agregar la sección de prisma al archivo package.json

package.json
"prisma": {
      "seed": "ts-node prisma/seed.ts"
    }

Finalmente ejecutar el script

> Terminal
$ npx prisma db seed

Crear el servicio de prisma

> Terminal
npx nest generate module prisma
npx nest generate service prisma

Esto genera la carpeta ./src/prisma y los archivos prisma.module.ts y prisma.service.ts en su interior.

Estos debeen tener el siguiente contenido.

src/prisma/prisma.module.ts
import { Module } from '@nestjs/common';
import { PrismaService } from './prisma.service';
 
@Module({
  providers: [PrismaService],
  exports: [PrismaService]
})
export class PrismaModule { }
src/prisma/prisma.service.ts
import { Injectable } from '@nestjs/common';
import { PrismaClient } from '@prisma/client';
 
@Injectable()
export class PrismaService extends PrismaClient { }

Configurar swagger

Se agregará swagger a la aplicación para documentar los endpoints y poder probarlos.

> Terminal
npm install --save @nestjs/swagger swagger-ui-express

Agregar la sección swagger a la configuración de la aplicación en main.ts

src/main.ts
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger';
 
async function bootstrap() {
  const app = await NestFactory.create(AppModule);
 
  const config = new DocumentBuilder()
    .setTitle('NestJS Prisma API')
    .setDescription('Descripción de NestJS Prisma API')
    .setVersion('1.0')
    .addTag('nestjs-prisma')
    .build();
 
  const document = SwaggerModule.createDocument(app, config);
  SwaggerModule.setup('docs', app, document);
 
  await app.listen(3000);
}
bootstrap();

Con esto al abrir la url http://localhost:3000/docs (opens in a new tab) se podrá ver la documentación de la api.

Generar el recurso de usuarios

> Terminal
npx nest generate resource users

Esto genera la carpeta ./src/users con varios archivos entre ellos users.service, users.controller.ts y users.module.ts

Listar usuarios

Lo primero que haremos será listar todos los usuarios de la base de datos. para eso modificaremos el archivo users.module.ts

src/users/users.module.ts
import { Module } from '@nestjs/common';
import { UsersService } from './users.service';
import { UsersController } from './users.controller';
import { PrismaModule } from 'src/prisma/prisma.module';
 
@Module({
  controllers: [UsersController],
  providers: [UsersService],
  imports: [PrismaModule],
})
export class UsersModule { }

Y el archivo users.service.ts

src/users/users.service.ts
import { Injectable } from '@nestjs/common';
import { CreateUserDto } from './dto/create-user.dto';
import { UpdateUserDto } from './dto/update-user.dto';
import { PrismaService } from 'src/prisma/prisma.service';
 
@Injectable()
export class UsersService {
  constructor(private prisma: PrismaService) { }
 
  create(createUserDto: CreateUserDto) {
    return 'This action adds a new user';
  }
 
  findAll() {
    return this.prisma.user.findMany();
  }
 
  findOne(id: number) {
    return `This action returns a #${id} user`;
  }
 
  update(id: number, updateUserDto: UpdateUserDto) {
    return `This action updates a #${id} user`;
  }
 
  remove(id: number) {
    return `This action removes a #${id} user`;
  }
}

Obtener un usuario por id

Agregaremos un endpoint para obtener un usuario por id.

  • GET /users/:id *

Para esto modificaremos el archivo users.controller.ts

src/users/users.controller.ts
@Get(':id')
  findOne(@Param('id') id: string) {
    return this.usersService.findOne(id);
  }

Y el archivo users.service.ts

src/users/users.service.ts
  findOne(id: string) {
    return this.prisma.user.findUnique({ where: {id}})
  }

Se puede probar yendo a http://localhost:3000/api (opens in a new tab). Haciendo clic en el menú desplegable GET /users/{id}. Se presiona Probar, se agrega un valor válido al parámetro id y se presiona Ejecutar para ver el resultado.

Crear un usuario

POST /users

Agregaremos el siguiente contenido al archivo users/dto/create-user.dto.ts

src/users/dto/create-user.dto.ts
import { ApiProperty } from '@nestjs/swagger';
 
export class CreateUserDto {
  @ApiProperty()
  name: string;
 
  @ApiProperty()
  email: string;
 
  @ApiProperty()
  password: string;
}

Y modificaremos el archivo del servicio users.service.ts

src/users/users.service.ts
  create(createUserDto: CreateUserDto) {
    return this.prisma.user.create({ data: createUserDto });
  }

Actualizar un usuario

PATCH /articles/:id

Para esto modificaremos el archivo del servicio users.service.ts

src/users/users.service.ts
  update(id: string, updateUserDto: UpdateUserDto) {
    return this.prisma.user.update({ 
      where: { id }, 
      data: updateUserDto 
    });
  }

Y el controlador users.controller.ts

src/users/users.controller.ts
  @Patch(':id')
  update(@Param('id') id: string, @Body() updateUserDto: UpdateUserDto) {
    return this.usersService.update(id, updateUserDto);
  }

Eliminar un usuario

DELETE /articles/:id

Modificaremos el archivo del servicio users.service.ts para agregar el método remove

src/users/users.service.ts
  remove(id: string) {
    return this.prisma.user.delete({ where: { id } });
  }

Y el controlador users.controller.ts

src/users/users.controller.ts
  @Delete(':id')
  remove(@Param('id') id: string) {
    return this.usersService.remove(id);
  }

Feliz código.




Comentarios