No tienes acceso a esta clase

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

No se trata de lo que quieres comprar, sino de quién quieres ser. Invierte en tu educación con el precio especial

Antes: $249

Currency
$209

Paga en 4 cuotas sin intereses

Paga en 4 cuotas sin intereses
Suscríbete

Termina en:

11 Días
3 Hrs
29 Min
51 Seg

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 43

Preguntas 3

Ordenar por:

¿Quieres ver más aportes, preguntas y respuestas de la comunidad?

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 };
  }

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 😄.

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;
  }

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];
  }
```js @Inject() private productsService: ProductsService; ```La dependencia también puede ser inyectada por medio del decorador @Inject()  @Inject() private projectsService: ProjectsService; Momento de la inyección: Constructor: La dependencia se inyecta al momento de crear la instancia de la clase. @Inject: La dependencia se inyecta después de la creación de la instancia de la clase, durante la fase de "inyección de dependencias". Flexibilidad: Constructor: Menor flexibilidad, la clase está "atada" a una implementación específica de la dependencia. @Inject: Mayor flexibilidad, la clase no está "atada" a una implementación específica de la dependencia. Ventajas: Constructor: Código más simple y directo. No se requiere configuración adicional. @Inject: Mayor flexibilidad para cambiar la implementación de la dependencia. Facilidad para probar la clase en aislamiento (se puede inyectar un mock de la dependencia). Desventajas: Constructor: Dificultad para probar la clase en aislamiento (se requiere un mock de la dependencia). Menor flexibilidad para cambiar la implementación de la dependencia. @Inject: Código más complejo y menos intuitivo. Se requiere configuración adicional (definir el proveedor de la dependencia). ¿Cuándo usar? **constructor:** si la clase necesita una dependencia específica y no necesita flexibilidad para cambiarla. **decorador @Inject:** si la clase necesita flexibilidad para cambiar la implementación de la dependencia o si necesita probar la clase en aislamiento.
updateProduct(id: number, productUpdated) {
    const newProductUpdated: Product = {
      id: id,
      ...productUpdated,
    };
    return this.products.splice(id, 1, newProductUpdated);
  }

Yo lo hice de esta manera no se si buena forma de hacerlo yo creo que si, dejenme saber cualquier cosa

Aunque posteriormente esto se terminará cambiando a una verdadera conexión a la base de datos, esta es una manera más reducida de hacer nuestro autoincremental:

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

crear put for changes in the same product

Aquí mi implementación del método delete()

delete(id: number) {
    const product = this.findOne(id);
    if (product) {
      const index = this.products.findIndex((item) => item.id === id);
      this.products.splice(index, 1);
      return this.products;
    }
    return null;
  }

Esta fue mi solucion al update y delete


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

  delete(id: number) {
    const indexProduct = this.products.findIndex((item) => item.id == id);
    if (indexProduct != -1) {
      this.products.splice(indexProduct, 1);
    }
  }

Asi fue mi solucion:

Mi implementacion de los metodos update y delete

  update(id: number, payload: any){
    const idx = this.products.findIndex((p) => p.id === id)    
    if(idx !== -1){
      return null
    }
    this.products[idx] = { ...this.products[idx], ...payload}
    return this.products[idx]
  }
  delete(id: number){ 
    const idx = this.products.findIndex((p) => p.id === id)
    delete this.products[idx]
  }

Pienso que podría ser más práctico, que el método findOne, retornara el elemento y el indice; en caso de existir el elemento. De esa forma, se evitaría hacer búsquedas secuenciales.
También podría ser mas útil, si la colección de elementos fuera un Map, ya que el id es único.

Por aquí les comparto mi código del update y el delete

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

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

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