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 42

Preguntas 3

Ordenar por:

驴Quieres ver m谩s aportes, preguntas y respuestas de la comunidad?

o inicia sesi贸n.

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];
  }
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 鈥淥jo 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