No tienes acceso a esta clase

¡Continúa aprendiendo! Únete y comienza a potenciar tu carrera

Implementando servicios en tu controlador

15/23
Recursos

Los servicios son el otro 50% de los controladores. Podría decirse que un controlador siempre hará uso de uno o más servicios para implementar lógica de negocio. Veamos cómo se relacionan.

Inyección de dependencias

Antes de hablar de la relación entre servicios y controladores, hay que hablar del patrón de diseño que NestJS utiliza internamente.

Imagínate que tienes un Servicio A que utiliza el Servicio B y este a su vez utiliza el Servicio C. Si tuvieses que instanciar el Servicio A, primero deberías instanciar el C para poder instanciar el B y luego sí hacerlo con el A. Se vuelve confuso y poco escalable si en algún momento también tienes que instanciar el Servicio D o E.

La inyección de dependencias llega para solucionar esto, resolver las dependencias de una clase por nosotros. Cuando instanciamos en el constructor el Servicio A, NestJS internamente crea automáticamente la instancia del servicio B y C sin que nosotros nos tengamos que preocupar por estos.

Controladores y servicios

Los controladores inyectan los servicios desde el constructor. De esta manera, cada endpoint puede hacer uso de la lógica del servicio.

import { Controller, Get } from '@nestjs/common';
import { AppService } from './app.service';

@Controller()
export class AppController {

  constructor(
    private readonly appService: AppService
  ) {}

  @Get()
  getHello(): string {
    return this.appService.getHello();
  }
}

Importa los servicios que necesites, pero hazlo de una manera controlada para mantener la escalabilidad de tu proyecto. Si necesitas importar 20 servicios en un mismo controlador, tal vez tengas que mejorar la estructura del proyecto.

Controllers

// src/controllers/products.controller.ts

import { ProductsService } from './../services/products.service';


@Controller('products')
export class ProductsController {
  constructor(private productsService: ProductsService) {}

  @Get()
  getProducts(...) {
    return this.productsService.findAll();
  }

  @Get(':productId')
  getOne(...) {
    return this.productsService.findOne(+productId);
  }

  @Post()
  create(..) {
    return this.productsService.create(payload);
  }

  @Put(':id')
  update(...) {
    return this.productsService.update(+id, payload);
  }

}

Refactor update

// src/services/products.service.ts
 update(id: number, payload: any) {
    const product = this.findOne(id);
    if (product) {
      const index = this.products.findIndex((item) => item.id === id);
      this.products[index] = {
        ...product,
        ...payload,
      };
      return this.products[index];
    }
    return null;
  }

Contribución creada por: Kevin Fiorentino.

Aportes 33

Preguntas 3

Ordenar por:

Los aportes, preguntas y respuestas son vitales para aprender en comunidad. Regístrate o inicia sesión para participar.

Los controladores tienen un fallo, sucede que el tipado funciona para la programación, pero al transpilarse sigue siendo JavaScript, por lo que los parámetros id siguen siendo strings y al operar con find en el array no retorna el objeto porque compara number === string, podemos evitar esto con los pipes de nestjs y la implementación quedaría así

import { ParseIntPipe } from '@nestjs/common';
  @Get(':id')
  getOne(@Param('id', ParseIntPipe) id: number) {
    return { msg: `Producto ${id}`, body: this.service.findOne(id) };
  }

De esta forma los parámetros llegarán con el tipo correcto, sugiero probar con typeof por si existen dudas.

Para incluir un servicio en un controlador usas el patrón de inyección de dependencias constructor(private productsService: ProductsService) {}

Estos son los métodos que yo implementé:


findOne(id: number) {
    const productFinded = this.products
      .filter((p: Product) => p.id === id)
      .shift();
    if (productFinded) return { success: true, product: productFinded };
    return {
      success: false,
      message: 'Product not found',
    };
  }

  create(payload: any) {
    const newProduct = { id: this.products.length + 1, ...payload };
    this.products.push(newProduct);
    return { success: true, product: newProduct };
  }

  update(id: number, payload: any) {
    this.products = this.products.map((p: Product) => {
      if (p.id === id) {
        return { ...p, ...payload };
      } else return p;
    });
    return this.findOne(id);
  }

  delete(id: number) {
    let productFinded = this.findOne(id);
    if (productFinded.success) {
      this.products = this.products.filter((p: Product) => p.id !== id);
    }
    return { success: productFinded.success };
  }

La reutilización del findOne no la consideré necesaria, así que hice todo con el findIndex

update(id: number, payload: any) {
    const index = this.products.findIndex((product) => product.id == id);
    if (index >= 0) {
      this.products[index] = {
        ...this.products[index],
        ...payload,
      };

      return this.products[index];
    }

    return null;
  }

Aquí va mi pequeño aporte. Para evitar que el usuario cambie el id desde el body podemos agregar la propiedad id al final del objeto que estamos actualizando.

update(id: number, payload: any) {
    const product = this.findOne(id);
    if (product) {
      const productIndex = this.products.findIndex((p) => p.id === product.id);
      this.products[productIndex] = {
        ...product
        ...payload,
        id, // Agregar esta línea
      };

      return product;
    }
    return null;
  }

Esto evitará que se sobrescriba la propiedad id 😄.

Muy bien explicado, en realidad por mi parte recomiendo seguir con este curso con todas las ganas. Está muy interesante

Este es mi método Update:

update(id: number, payload: any) {
    const index = this.products.findIndex((item) => item.id === id);
    if (!this.products[index]) return { message: 'Id no existe' };
    this.products[index] = {
      ...this.products[index],
      ...payload,
    };
    return this.products[index];
  }

Estos son los metodos que yo implemente

    update(id: number, payload: any) {
        const User = this.findOne(id);
        const index = this.Users.indexOf(User);
        this.Users[index] = { ...this.Users[index], ...payload };
        return this.Users[index];
    }
    delete(id: number) {
        const User = this.findOne(id);
        const index = this.Users.indexOf(User);
        this.Users.splice(index, 1);
        return true;
    }

Se parece a Angular

Hasta el momento he entendido todo muy claro, excelente profesor…

Aporto mi implementación de servicio de Productos.

import { Injectable } from '@nestjs/common';
import { Product } from '../../entity/product.entity';

@Injectable()
export class ProductsService {
  private counterId: number;
  private products: Product[] = [
    {
      id: 1,
      name: 'Product 1',
      description: 'bla bla',
      price: 200,
      stock: 50,
      image: '',
    },
  ];
  constructor() {
    this.counterId = 1;
  }

  findAll() {
    return this.products;
  }

  findOne(id) {
    return this.products.find((item) => item.id === id);
  }

  create(payload: any) {
    this.counterId = this.counterId + 1;
    const newProduct = {
      id: this.counterId,
      ...payload,
    };
    this.products.push(newProduct);
    return newProduct;
  }

  update(payload: any, id: number) {
    const toEdit = this.findOne(id);
    const keys = Object.keys(payload);
    const values = Object.values(payload);
    let counter = 0;
    let temp = [];
    values.forEach((element) => {
      if (!element) {
        temp.push(counter);
        return counter++;
      }
      toEdit[keys[counter]] = values[counter];
      counter++;
    });
    return this.products;
  }

  erase(id: any) {
    const toErase = this.findOne(id);
    const index = this.products.indexOf(toErase);
    console.log(index, 'Erase method');
    return this.products.splice(index, 1);
  }
}

Mi codigo:

update(id: number, payload: any) {
    const index = this.products.findIndex((item) => item.id == id);
    if (index == -1) {
      return null;
    }
    this.products[index] = {
      ...this.products[index],
      ...payload,
    };
    return this.products[index];
  }

  delete(id: number) {
    this.products = this.products.filter((item) => item.id != id);
    return this.products;
  }

Acá les dejo mi propuesta


  findAll(): Product[] {
    return this.products;
  }

  findOne(id: number): Product {
    return this.products.find((product) => product.id === id);
  }

  create(product: Product): Product {
    this.products.push({ ...product, id: this.counterId++ });
    return product;
  }

  update(id: number, product: Product): Product {
    const index = this.products.findIndex((item) => item.id === id);
    this.products[index] = product;
    return product;
  }

  delete(id: number): Product {
    const index = this.products.findIndex((p) => p.id === id);
    const product = this.products[index];
    this.products.splice(index, 1);
    return product;
  }

Si solo usamos el verbo PUT creo que en el update debemos considerar si se envía todo el producto completo o solo parcial “Ojo en las actualizaciones parciales lo mejor es usar patch” pero creo que este ejemplo es bueno.

y mi solucion la dejo en aqui:

import { Injectable } from '@nestjs/common';
import { Product } from 'src/entities/products/product.entity';

@Injectable()
export class ProductsService {
  private counterId = 2;
  private products: Product[] = [
    {
      id: 1,
      name: 'Torta de chorizo',
      description:
        'Torta de chorizo de malpaso con repollo, cebolla, jitomate, aguacate y chile',
      price: 40,
      stock: 200,
      image: null,
      deleted: false,
      active: true,
      private: false,
    },
    {
      id: 2,
      name: 'Rufles',
      description: 'Papa de sabritas sabor original, colo azul',
      price: 15,
      stock: 10,
      image: null,
      deleted: false,
      active: true,
      private: false,
    },
  ];
  findAll() {
    return this.products;
  }
  findOne(id: number) {
    return this.products.find((r) => r.id == id);
  }
  create(payload: any) {
    this.counterId += 1;
    const newProduct = {
      id: this.counterId,
      ...payload,
    };
    this.products.push(newProduct);
    return newProduct;
  }
  update(id: number, payload: any) {
    const product = this.findOne(id);
    if (product) {
      return Object.assign(product, payload);
    }
    return false;
  }
  delete(id: number) {
    this.products = this.products.filter((r) => Number(r.id) !== Number(id));
    return id;
  }
}

asi?

    delete(id:number) {
        let productToDelete = this.products.find((item) => item.id === id)
        let productToDeleteIndex = this.products.indexOf(productToDelete)
        return productToDelete ? this.products.splice(productToDeleteIndex) : 'There is no such a product handling function'
    }
update(id: number, payload: UpdateProductInterface): Array<Product> {
  const updated = [];
  let currentProduct = null;
  this.products = this.products.map((p: Product) => {
    if (p.id === id) {
      currentProduct = { ...p, ...payload };
      updated.push(currentProduct);
      return currentProduct;
    } else {
      return p;
    }
  });
  return updated;
}
update(id: number, data) {
		const index = this.products.findIndex(product => product.id === id)
		this.products[index] = { id, ...data }
		return id
	}

Otra forma de implementar el metodo update desde el service.

  update(id: string, payload: Product) {
    const product = this.findOne(id);

    if (product) {
      Object.assign(product, payload);
      return product;
    }

    return null;
  }

Corrigiendo el servicio Update

Pruebas del uso de los servicios en el controlador

agregar el servicio Update en el Controlador

Crear el servicio de Update

Crear nuevo producto

Retornar o mostrar un solo producto

Retornar o mostrar todos los productos

integrar un Servicio en el Controlador

Esto fue parte de mi codigo implementado para la actualización

update( id: string, payload: Publication ): Publication {

        this.publications = this.publications.map( 
                                        ( publication ) => 
                                            publication.id === id 
                                                ? publication = { ...publication, ...payload } 
                                                : publication 
                            );

        return payload;
}

Hola 👋

Les comparto mi implementacion, aunque claro no tiene validaciones aun 😅

findAll() {
    return this.products;
  }
  findById(id: number) {
    const products = this.products.filter((product) => product.id === id);
    if (products.length) return products[0];
    return null;
  }
  create(payload: any) {
    const newProduct = { id: this.products.length + 1, ...payload };
    this.products.push(newProduct);

    return newProduct;
  }
  update(id: number, payload: any) {
    this.products = this.products.map((product) => {
      if (product.id === id) return { ...product, ...payload };
      else return product;
    });

    return this.findById(id);
  }
  delete(id: number) {
    const productFinded = this.findById(id);
    if (productFinded)
      this.products = this.products.filter(
        (product: Product) => product.id !== id,
      );

    return productFinded;
  }

Espero haberles aportado algo 🚀

👏

Creo que mis compañeros tiene razón. estaríamos utilizando dos iteraciones sin razón cuando ya findIndex nos retorna un -1 para saber si existe o no ese item.

observando el código de mis compañeros y optimizándolo un poco, yo propondría la siguiente solución:

    update(id: number, payload: Product) {
        const index = this.products.findIndex(item => item.id === id);
        if ( index === -1 ) {
            return null;
        }
        this.products[index] = {
            ...this.products[index],
            ...payload
        }
        return this.products[index];
    }

En el update, para evitar hacer dos Find y que el codigo sea mas eficiente, lo hice de esta forma:

update(id:number, payload:any) {
  //Buscamos si tiene un index ese objeto...
  const index = this.products.findIndex((item) => item.id === id);
  if(index != -1){
    //Al producto en la posicion index, vas a actualizarlo con el payload
    this.products[index] = payload;
    return this.products[index];
  }

  //Si no existe el producto
  return null;
}

Mi solución a el reto anterior, no sé si esté bien aún, no sé como probarlo jaja

  filter(id: number) {
    this.products = this.products.filter((el) => el.id !== id);
  }
  delete(id: number, payload: any) {
    this.products = this.products.map((el) => {
      if (el.id === id) {
        return {
          id,
          ...payload,
        };
      } else {
        return el;
      }
    });
  }

creo que la implementacion del PUTesta mal según la referencia de RFC 2616 y RFC 5789
donde PUT dice que idenpotente y deberia haber usado el metodo PATH y no PUT debido lo idenpotente