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
16 Hrs
44 Min
47 Seg

Introducción a servicios: crea tu primer servicio

14/27
Recursos

Aportes 69

Preguntas 18

Ordenar por:

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

Les dejo mis apuntes de la clase 🙈

Así entendí “clean arquitecture”.

Me encantaría leer correcciones.

Uff hasta ahora el curso me ha encantado, super claro y bien estructurado. Asi va mi codigo por ahora, me ha parecido muy buena la parte de encapsular el funcionamiento en clases 😄

Products.routes

const express = require('express');
const router = express.Router();
const productService = require('../services/product.service');
const product = new productService();

router.get('/', (req, res) => {
    let {limit, offset} = req.query;
    res.json(product.find({offset, limit}));
});

router.get('/:id', (req, res) => {
    const {id} = req.params;
    res.json(product.findOne(id));
});


router.post("/", (req, res) => {
    const {name, price, image} = req.body;

    const newProduct = product.create({name, price, image});
    if (newProduct) {
        res.status(201).json({
            message: "Product added",
            data: newProduct
        });

    } else
        res.status(501).json({message: "internal error"});
});


router.patch("/:id", ((req, res) => {
    const id = parseInt(req.params.id);
    const {name, price, image} = req.body;
    const updateProduct = product.update(id, {name, price, image});
    if (updateProduct) {
        res.json({
            message: "Product updated",
            data: req.body
        });
    } else {
        res.status(501).json({message: "Internal error"})
    }


}));

router.delete("/:id", (req, res) => {
    const id = parseInt(req.params.id);
    const currentProduct = product.delete(id);
    if (currentProduct) {
        res.status(201).json({message: "Product deleted", data: currentProduct});
    } else {
        res.status(404).json({message: "Product not found"});
    }
});


module.exports = router;

prduct.service.js

const faker = require("faker");

class ProductService {

    constructor() {
        this.products = [];
        this.faker();
    }

    faker(quantity = 100) {
        for (let i = 0; i < quantity; i++) {
            this.products.push({
                id: i + 1,
                name: faker.commerce.productName(),
                price: parseInt(faker.commerce.price(), 10),
                image: faker.image.imageUrl()
            });
        }

    }

    create(product = {}) {
        const newProduct = {
            id: this.products[this.products.length - 1].id + 1,
            name: product.name,
            price: parseInt(product.price),
            image: product.image
        }
        this.products.push(newProduct);

        return newProduct;
    }

    find(filter = {}) {
        return this.products.slice(filter.offset | 0, filter.limit ? parseInt(filter.limit) + parseInt((filter.offset | 0)) : undefined)
    }


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

    update(id, changes = {}) {
        const product = this.products.find((prod) => prod.id === id);
        const idx = this.products.findIndex((prod) => prod.id === id);
        if (product) {
            this.products[idx] = {
                id: product.id,
                name: changes.name || product.name,
                price: changes.price || product.price,
                image: changes.image || product.image
            };
            return product;
        }
    }


    delete(id) {
        const product = {...this.products.find((prod) => prod)};
        if (product) {
            this.products = this.products.filter((p) => p.id !== id);
            return product;
        }
    }
}

module.exports = ProductService;

Actualmente el módulo random de @faker-js/faker está @deprecated, y se recomienda usar en su lugar:

faker.string.uuid()

para generar un id.

Resumen de la clase:
Concepto
Los servicios es donde encapsulamos todos los casos de usos y comenzar a interactuar con la lógica de negocio.
En el caso de una tienda: hacer compras, transacciones, etc.
.
Estructura
Esta arquitectura está definida por capas.
Entidades:

  • En esta capa encontramos las entidades base del negocio.
  • En nuestro caso: productos, categorías, órdenes de compra.

Casos de uso

  • En esta capa tenemos lo relacionado a la lógica de negocio
  • En esta capa se encuentra los servicios

Controladores

  • En esta capa se brinda el acceso.
  • Aquí encontramos el routing

.

Flujo de trabajo:

  • Controladores: Encontramos los routes y middlewares.
  • Los controladores acceden a la capa de servicios
  • Servicios: donde se encuentra la lógica de negocio
  • Los servicios usan las librerías.
  • Las librerías se encargan de contactarse a la capa de entidades
  • Las librerías se contactan a otras fuentes de datos: API externa o base de datos.

Me está encantando este curso, cómo es que lo explica el profesor.
Incluso me estoy interesando mucho por el mundo del backend. 😁

Para la peticion get que busca por id una manera de validar primero si hay algun valor retornado se puede hacer de la siguiente manera:

router.get('/:id', (req, res) => {
  const { id } = req.params;

  const product = service.findOne(id);

  if(!product){
    res.status(404).json({
      message: `El producto con id ${id} no existe`
    });
  } else {
    res.status(200).json({
      product
    });
  }

});

De esta manera nos aseguramos que traiga valor la consulta de lo contraria manejamos la respuesta que no encontró el objeto buscado.

👀 OJO:
Ya que el profesor menciona mucho el tema de las convenciones a la hora de nombrar las cosas, aqui les dejo esta convención:

  • Las clases se nombran en singular y usando PascalCase:
    • Ejemplo: Persona, Producto, Categoria, o en en el caso de esta clase debería ser ProductService.
  • Las funciones se nombran en verbos (crear, agregar, actualizar, etc) y usando camelCase:
    • Ejemplo: crearProducto(), agregarCategoria(), eliminarProducto()

Tomen nota ✍️😉

Este profesor es muy bueno, el tema de los servicios siempre me habían confundido cundo los usábamos en otros cursos de la Escuela de JS.

De una vez agregue una validación si no encuentra el id para retornar un 404 (yo lo estoy haciendo con books en vez de products):

router.get('/:id', (req, res) => {

    const { id } = req.params;
    const book = service.findOne(id);

    if ( book ) {
        res.status(200).json({
            message: "Libro encontrado",
            book        
        })
    } else {
        res.status(404).json({
            message: "Libro no encontrado :( ",
        })
    }
}) 

Hasta este punto me siento super bien, con la forma de explicar de este profe, grande Nico!

Les comparto mi código con mejoras para realizar un limit

Hola, a todos! A la fecha, 10-01-24 faker funciono de esta forma: ```js generate() { const limit = 100; for(let index = 0; index < limit; index++) { this.products.push({ id: faker.string.uuid(), name: faker.commerce.productName(), price: parseInt(faker.commerce.price(), 10), image: faker.image.url(), }); }; }; ```

Felicitationes Nicolas, excelente profesor.

Excelente, ahora entiendo cuándo se refieren a servicios.

muchachos, solucione el error que tenia, sucede que debemos instalar el este npm i [email protected] sucede que los otros dan problema al momento de hacer get

mas profesores así en Platzi.

Servicios Web
Nacen de la necesidad de cubrir la lógica de negocio.
Lógica de negocio
El término lógica de negocio hace referencia a la parte de un sistema que se encarga de codificar las reglas de negocio del mundo real que determinan cómo la información puede ser creada, almacenada y cambiada.

  • En el ámbito de la programación, es una de las capas del modelo MVC Modelo Vista-Controlador separando así la complejidad del desarrollo en capas independientes.

Servicios
Los servicios es donde encapsulamos todos los casos de usos y comenzamos a interactuar con la lógica de negocio.

  • En el caso de una tienda: hacer compras, transacciones, etc.

Los servicios deben deben ser construidos por una arquitectura, que en este caso será:
Clean Architecture
Clean architecture es un conjunto de principios cuya finalidad principal es ocultar los detalles de implementación a la lógica de dominio de la aplicación.

De esta manera mantenemos aislada la lógica, consiguiendo tener una lógica mucho más mantenible y escalable en el tiempo.


Capas
-Entidades:

En esta capa encontramos las entidades base del negocio.
En nuestro caso: productos, categorías, órdenes de compra.
-Casos de uso

En esta capa tenemos lo relacionado a la lógica de negocio
En esta capa se encuentra los servicios
-Controladores

En esta capa se brinda el acceso.
Aquí encontramos el routing.

Flujo de trabajo:

  • Controladores: Encontramos los routes y middlewares.
  • Los controladores acceden a la capa de servicios
  • Servicios: donde se encuentra la lógica de negocio
  • Los servicios usan las librerías.
  • Las librerías se encargan de contactarse a la capa de entidades
  • Las librerías se contactan a otras fuentes de datos: API externa o base de datos.

Implementando el query limit a products.
.
products.service

const faker = require('faker');

class ProductsService {
  constructor() {
    this.products = [];

    const limit = 1000;
    this.generate(limit);
  }

  generate(limit) {
    for (let index = 0; index < limit; index++) {
      this.products.push({
        id: faker.datatype.uuid(),
        name: faker.commerce.productName(),
        price: parseInt(faker.commerce.price(), 10),
        image: faker.image.imageUrl(),
      });
    }
  }

  create() {}

  find(limit) {
    return this.products.slice(0, limit);
  }

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

  update() {}
  delete() {}
}

module.exports = ProductsService;


products.router

const express = require('express');

const ProductsService = require('./../services/products.service');

const router = express.Router();
const service = new ProductsService();

router.get('/', (req, res) => {
  const { limit } = req.query;
  const products = service.find(limit || 10);
  res.json({
    size: products.length,
    products,
  });
});

router.get('/filter', (req, res) => {
  res.send('Yo soy un filter');
});

router.get('/:id', (req, res) => {
  const { id } = req.params;
  const product = service.findOne(id);
  if (!product) {
    res.status(404).json({
      message: `El producto con id '${id}' no existe`,
    });
  } else {
    res.status(200).json(product);
  }
});

.
Sin limit

Con limit

por aqui nadie responde, tengo un error, no logra encontrar el produto por idconst faker = require('faker'); // instalar el npm install @faker-js/faker --save-devclass ProductsServices { constructor(){ this.products = \[] this.generate(); } generate(){ for (let index = 0; index < 20; index++) { this.products.push({ id: faker.datatype.uuid(), name: faker.commerce.productName(), price: parseInt(faker.commerce.price(), 10), img: faker.image.urlLoremFlickr }) } } create() { } find() { return this.products; } findOne(id) { return this.products.find(item => item.id === id); } update() { } delete() { }} module.exports = ProductsServices ```js const faker = require('faker'); // instalar el npm install @faker-js/faker --save-dev class ProductsServices { constructor(){ this.products = [] this.generate(); } generate(){ for (let index = 0; index < 20; index++) { this.products.push({ id: faker.datatype.uuid(), name: faker.commerce.productName(), price: parseInt(faker.commerce.price(), 10), img: faker.image.urlLoremFlickr }) } } } find() { return this.products; } findOne(id) { return this.products.find(item => item.id === id); } } module.exports = ProductsServices ```

No olviden colocar el new cuando instancian service. Estuve 30 minutos buscando el error, hasta que tuve que chequear el cod del profesor.
Y si se olvidan que se instancia con new, no se olviden de leer la terminal; estaba tan ansioso en buscar el error que identificaba el donde, y no vi que literalmente la terminal me decia:

const service = ProductService();
                ^

TypeError: Class constructor ProductService cannot be invoked without 'new'   
`{` `id: faker.string.uuid(),` `}`

Wowww, este curso me ha hecho amar el backend, gracias!

La forma de importar las funcionalidedes de @faker-js/faker, al día de hoy son:

export function createRandomUser(): User {
  return {
    userId: faker.string.uuid(),
    username: faker.internet.userName(),
    email: faker.internet.email(),
    avatar: faker.image.avatar(),
    password: faker.internet.password(),
    birthdate: faker.date.birthdate(),
    registeredAt: faker.date.past(),
  };
}

Otra forma muy válida de los servicios (o también llamados controladores) es trabajar con funciones los callbacks. El código queda limpio igualmente y puede ser una mejor introducción para los chicos/as que están iniciando con esta forma de programar de forma modular.

Tengan en cuenta que debido a los cambios en la libreria Faker ya no se usa el

faker.datatype.uuid

sino

faker.string.uuid

les dejo link de la info:
https://fakerjs.dev/api/datatype.html#uuid

El código quedó medio raro porque use mucho la palabra “service” 😂, pero ya lo había definido así

class ServicesService {

  constructor(){
    this.services = [
      {
        id: '1',
        service: "Diseño de Cejas",
        category: "Cejas",
        price: 7000,
        durationMin: 5
      },
      {
        id: '2',
        service: "Pigmentación de Cejas",
        category: "Cejas",
        price: 5000,
        durationMin: 15
      },
      {
        id: '3',
        service: "Corte de cabello",
        category: "Cabello",
        price: 20000,
        durationMin: 30
      },
      {
        id: '4',
        service: "Secado de cabello",
        category: "Cabello",
        price: 12000,
        durationMin: 30
      }
    ];

  }

  create() {};
  find() {
    return this.services;
  };
  findOne(id) {
    return this.services.find(item => item.id === id );
  };
  update() {};
  delete() {};
};

module.exports = ServicesService;

Me conflictua bastante ver POO en JS , cuando JS no es un lenguaje orientado a objetos, si no funcional. me gustaria debatir esto y de verdad JS no POO o OOP

const { faker } = require("@faker-js/faker");

class ProductsService {

  constructor(){
    this.products = [];
    this.generate();
  }

  generate(){
    const limit = 100;
    for (let i = 0; i < limit; i++) {
      this.products.push({
        id: faker.datatype.uuid(),
        name: faker.name.fullName(),
        price: parseInt(faker.commerce.price(), 10),
        image: faker.image.imageUrl()
      });
    }
  }

  create(){

  }

  find(){
    return this.products;
  }

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

  update(){

  }

  delete(){

  }

}

module.exports = ProductsService;

APORTE

si no quieres ver en tus require lo siguiente

const example = require('./../services/product.service')

puedes crear un archivo llamado jsconfig.json en la raiz de tu proyecto con el siguiente codigo.

{
 "compilerOptions": {
   "baseUrl": "root_project",
 }
}

donde root_project sera el path de donde quieres llamar a todos tus archivos, ejemplo quiero que todos mis archivos sean llamados desde mi raiz, podemos colocar la siguiente configuracion.

{
 "compilerOptions": {
   "baseUrl": ".",
 }
}

Al estar el archivo jsconfig.json en la raiz el punto hace referencia a si mismo, pudiendo entonces requerir nuestro archivo service de ejemplo de la siguiente forma.

const example = require('services/product.service')

Evitar los import relativos nos puede ayudar a comprender mejor donde estan nuestros archivos requeridos,

Recuerden que si quieren usar Faker sin ningún inconveniente, deben descargarlo como

npm i faker@5.5.3

Especificando la versión, porque la 6.6.6 no funciona
Fecha: 13-2-2022

8:59

Un string super largo que va a actuar de forma randomnica

😭🤣

Aun no termino el curso y ya estoy maravillado de este curso, no entiendo como no lanzaron antes este curso, buenisimo el profesor y el contenido.

muy genial todo.

Acá les dejo el generador de usuarios con algunos campos importantes, espero les sirva 😄

generate(){
    const limit = 20;
    for(let i=0;i<limit;i++){
      this.users.push({
        id: faker.datatype.uuid(),
        name: faker.name.firstName(),
        lastName: faker.name.lastName(),
        avatar: faker.image.imageUrl(),
        city: faker.address.cityName(),
        email: faker.internet.email(),
      })
    }
  }

Excelente profesor. Muy bien explicado todo el curso hasta ahora un tema que en un principio me había resultado super complicado.

Les comparto mi código con mejoras para realizar un limit


class ProductsService{

  constructor(){
    this.products = [];
    this.generate();
  }

  generate(limit){
    console.log(limit)
    for(let index = 0; index<(limit||1000); index++){
    this.products.push({
      id:faker.datatype.uuid(),
      name:faker.commerce.productName(),
      price: parseInt(faker.commerce.price(),10),
      image:faker.image.imageUrl()

    });
    }
  }
  find(limit){
    return this.products.slice(0,limit)
  }
}

module.exports = ProductsService;```

```  const express  = require('express');
const  ProductsService = require('./../services/product.service')
const router = express.Router();
const service = new ProductsService();

router.get('/',(req,res)=>{
  let {limit,page} =req.query;
  limit = limit || 10;
  const products = service.find(limit);
  res.json({
    size:products.length,
    products})
})


module.exports = router;
¿Cuántas instancias deben crearse por clase en la capa de entidades o servicios? Por ej: cuantas instancias crear por la clase de ProductsService. Depende de los requerimientos de la aplicación: * **Única instancia (Patrón de diseño creacional Singleton)**: * Se usa una sola instancia si todas las partes de la aplicación comparten el mismo estado o tienen datos centralizados (por ejemplo, **<u>un inventario global de productos</u>**). * **Múltiples instancias**: * Se crean varias instancias si cada componente necesita manejar datos independientes o tener su propia lógica (por ejemplo, **<u>carritos de compra separados para cada usuario</u>**).
en la version 9 de faker a la hora de usar el id crashea el servidor  generate(){    const limit = 100    for (let index = 0; index < limit; index++){      this.products.push({        id: faker.string.uuid(),        name :faker.commerce.productName(),        price : parseInt(faker.commerce.price(),10),        image : faker.image.url()      })    }  } ```js [nodemon] app crashed - waiting for file changes before starting... ```la nueva forma de generar el ID es faker.string.uuid() El codigo completo quedaria de esta forma ```js generate(){ const limit = 100 for (let index = 0; index < limit; index++){ this.products.push({ id: faker.string.uuid(), name :faker.commerce.productName(), price : parseInt(faker.commerce.price(),10), image : faker.image.url() }) } } ```espero les sirva dejo el lick de esta documentacion <https://fakerjs.dev/guide/upgrading>
2024 Faker Generar Id: faker.string.uuid() Doc -https://fakerjs.dev/api/string#uuid
Para los que estan viendo la clase en tiempos recientes, los uuid de faker ahora se encuentran en string, asi que usen: ```js faker.string.uuid() ```
Por fin puedo desacoplar mi código. Muy buena clase.

faker.datatype.uuid()
está obsoleto, hay que usar en su lugar
faker.string.uuid()

Una pregunta el id y la image en faker me sale como deprecado que version estan ocupando ? gracias
Me cuesta mucho entender, además que en ocasiones se me rompe la conexión entre las rutas y servicios. Hasta ahora he repetido todo paso a paso y no encuentro el error.
Evité utilizar el **require** y lo reemplacé por **import**, me quedó de la siguiente manera: ```js import express from 'express'; import ProductService from '../services/product.service'; const router = express.Router(); const service = new ProductService(); router.get('/', (req, res) => { const products = service.find(); res.json(products) }) ```
Dios les bendiga, no se olviden de poner los parentesis y también de haber puesto la carpeta de services en el root jajaja. también acuerdense de los molestos this porque uno no cae en cuenta y por una pendejada te quedas mucho tiempo leyendo codigo viendo donde está el error y como ejemplo es this.products.id pero dice products.id tu piensas que está bien a pesar de no estar el this. Son cosillas que me hubiera gustado saber antes de estar estancado casi una hora xd
const faker = require('faker');

class ProductsService {

  constructor(){
    this.products = [];
    this.generate();
  }

  generate() {
    const limit = 100;
    for (let index = 0; index < limit; index++) {
      this.products.push({
        id: faker.datatype.uuid(),
        name: faker.commerce.productName(),
        price: parseInt(faker.commerce.price(), 10),
        image: faker.image.imageUrl(),
      });
    }
  }

  create() {

  }

  find() {
    return this.products;
  }

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

  update() {

  }

  delete() {

  }

}

module.exports = ProductsService;

cuando avancemos para hacerlo metodos para otras rutas, como la de users, categories, etc. tendriamos que repetir todas las funciones de crear, borrar y demas. por lo que es preferible que esos metodos sean una clase por si sola y cada una de nuestros services hereden esos metodos, asi ahorramos limeas y es mas mantenible, al final el crud va a funcionar casi igual para todos los services de ruta,

to lo hice de esta manera:

// este es mi service de propducts

//yo use la importacion de modulos, ustedes pueden importar como quieran

import { faker } from "@faker-js/faker";
import { CrudService } from "./crud.service.js";

class ProductsService extends CrudService {
  constructor() {
    super();
    this.generate();
  }

  generate() {
    for (let index = 0; index < 5; index++) {
      this.data.push({
        id: faker.datatype.uuid(),
        name: faker.commerce.productName(),
        price: parseInt(faker.commerce.price()),
        image: "url",
      });
    }
  }

//este template sirve para unos metodos que usare en el crud
  itemTemplate() {
    return {
      id: "",
      name: "",
      price: "",
      image: "",
    };
  }
}

export { ProductsService };

ahora la clase Crud


import { faker } from "@faker-js/faker";

class CrudService {
  constructor() {
//aqui tenemos el array de los datos, asi se hereda uno para cada service
    this.data = [];
  }

//recivo el item
  create(item) {
    try {
//le creo un id porque cuando creamos no debemos un item 
//desde el front no deveriamos mandar le id, 
//se deberia generar automatico
      const id = { id: faker.datatype.uuid() };
//el item que mandan le anidamos el id
      const newItem = {
        ...id,
        ...item,
      };

//ahora traemos las prop que deberia tener un item
      const props = Object.keys(this.itemTemplate());

//verificamos que el item tiene todas las props necesarias para ser guardado
      for (let prop of props) {
        if (!newItem.hasOwnProperty(prop)) {
//si no las tiene retirna el error
          throw new Error(`La propiedad '${prop}' no está presente.`);
        }
      }
//si esta todo bien agregamos el item a la data
      this.data.push(newItem);
//retorno si salio bien el porceso y el item qeu se aguardo
      return [true, newItem];
    } catch (error) {
      return [error];
    }
  }

  find() {
    return this.data;
  }

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


//recibo el id
  delete(id) {
    try {
//verifico que el id exista
      const existId = this.data.findIndex((item) => item.id === id);
      if (existId === -1) {
//si no existe retornamos error
        throw new Error(`No se encontró el ID '${id}'.`);
      } else {
//si existe entonces guardamos todos los items que no tengan ese id
        const updateProduct = this.data.filter((item) => item.id !== id);
//guardamos los daros actualizados
        this.data = [...updateProduct];
        return true;
      }
    } catch (error) {
      return error;
    }
  }
}

export { CrudService };


si tienen alguna correcion me pueden decir :3

La parte del NOT FOUND la hice de la siguiente manera, no sé si lo explican más adelante pero, acá lo dejo.

router.get('/:id', (req, res) =>{
  const { id } = req.params;
  const product = service.findOne(id);
  if(product){
    res.status(StatusCodes.OK).json(product);
  }else{
    res.status(StatusCodes.NOT_FOUND).json({message: 'Not Found'})
  }
});

muy Interesante esta clase

pura calidad 💚

Me gusta mucho cómo queda el código después de aplicar clean arquitecture 👏🏼

Mi solución antes de ver el siguiente vídeo:

products router

const express = require('express');
const router = express.Router();

const ProductsService = require('../services/product.service')
const service = new ProductsService();

router.get('/', (req, res) => {
  const products = service.findAll()
  res.json(products)
})

//! Rutas específicas ANTES que rutas dinámicas

router.get('/:id', (req, res) => {
  const { id } = req.params; //* tiene que ser igual que en los query params
  const product = service.findOne(id)
  res.json(product)
})

router.post('/', (req, res) => {
  const body = req.body
  const { name, price, image } = body
  service.create(name, price, image)

  res.status(201).json({
    message: 'product created',
    data: body
  })
})

router.patch('/:id', (req, res) => {
  const { id } = req.params
  const body = req.body
  const { name, price, image } = body
  service.update(id, name, price, image)

  res.json({
    message: 'updated partial',
    data: body,
    id
  })
})

router.delete('/:id', (req, res) => {
  const { id } = req.params
  service.delete(id)

  res.json({
    message: 'deleted',
    id
  })
})

module.exports = router;

products service:

const { faker } = require('@faker-js/faker')

class ProductsService {

  constructor() {
    this.products = []
    this.generate() //* Creará los productos iniciales
  }

  generate() {
    const limit = 100

    for(let index = 0; index < limit; index++) {
      this.products.push({
        id: faker.datatype.uuid(),
        name: faker.commerce.productName(),
        price: parseInt(faker.commerce.price(), 10),
        image: faker.image.imageUrl(),
      })
    }
  }

  create(name, price, image) {
    this.products.push({
      id: faker.datatype.uuid(),
      name,
      price,
      image
    })
  }

  findAll() {
    return this.products
  }

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

  update(id, name, price, image) {
    const product = this.findOne(id)

    name !== undefined ? product.name = name : null
    price !== undefined ? product.price = price : null
    image !== undefined ? product.image = image : null

    return product
  }

  delete(id) {
    const product = this.findOne(id)
    this.products.splice(product, 1)
  }
}

module.exports = ProductsService

Esto va excelente 😄

<
router.get('/:id', (req,res) => {
  const {id} = req.params;
  const product = service.findOne(id);
  if(product === undefined){
    res.status(404).json({
      message:'Product not found, please check your id'
    });
  }else{
    res.status(200).json(product);
  }

});
> 

yo le añadí validación al método findOne en caso de no encontrar el producto con el id recibido en los parámetros

findOne(id){
        const find = this.products.find(el => el.id == id)
        if (find){
            return find
        }

        return {
            message: 'not found'
        }
    }
class ProductsServices {
    constructor(){
        this.products = []
        this.generate()
    }

    generate(){
        for (let i = 0; i < 10; i++){
            this.products.push({
                id: faker.datatype.uuid(),
                name: faker.commerce.productName(),
                price: faker.commerce.price(),
                image: faker.image.imageUrl()
            })
        }
    }

    find(){
        return this.products
    }

    findOne(id){
        const find = this.products.find(el => el.id == id)
        if (find){
            return find
        }

        return {
            message: 'not found'
        }
    }
}

Yo lo hice con query params

router.get('/', (req, res)=>{
  const id  = req.query.id || null;
  if(id === null){
    const products = service.find();
    res.json(products)
  }
  else{
    const products = service.findOne(id);
    res.json(products);
  }
})

Existe una extensión en visual llamada Thunder, para hacer peticiones, no hace falta abrir otra cosa.

“Controllers” == “Routes”

Tengo una gran duda, si es que pongo este código:

generate() {

    let limit = 20

    while(limit > 0) {

      //Usamos los métodos de faker el cual nos genera datos aleatorios de un ecommerce
        this.products.push({

        id: parseInt(Math.random() * 23 ).toString() || faker.datatype.uuid(),
        name: faker.commerce.productName(),
        price: faker.commerce.price(),
        url: faker.image.imageUrl()
      })

      limit--
    }
  }

Esta todo bien, pero si le cambio el nombre a las keys por ejemplo por estas

generate() {

    let limit = 20

    while(limit > 0) {

      //Usamos los métodos de faker el cual nos genera datos aleatorios de un ecommerce
        this.products.push({

        id: parseInt(Math.random() * 23 ).toString() || faker.datatype.uuid(),
        productName: faker.commerce.productName(),
        price: faker.commerce.price(),
        productURL: faker.image.imageUrl()
      })

      limit--
    }

No me crea los registros, alguna idea?

Para el caso de la consulta de producto:
Recibo el id, instancio la clase y hago una validacion, en caso de encontrar el producto por el id que se envio retorno un status 200 mas el producto.
Caso constrario retorno un status 401 y un mensaje.

router.get('/:id', (req, res) => {
  const { id } = req.params
  const product = service.findOne(id)
  if(product) res.status(200).json(product)
  else res.status(401).json({message:"not found"})
})

Generador para Categorias

  generate() {
    const limit = 10;

    for (let i = 0; i < limit; i++) {
      this.categories.push({
        id: faker.datatype.uuid(),
        nombre: faker.commerce.productMaterial()
      });
    }
  }

Una validación simple para cuando se requiera un producto inexistente:

router.get('/:id', (req, res) => {
  const { id } = req.params;
  const product = service.findOne(id);
  if (product) {
    res.status(200).json(product);
  } else {
    res.status(404).json({ message: 'Product not found' });
  }
});

Creo que tenía una muy mala práctica y es que combinaba la capa de servicios con los controladores.

  • Clean Arquitecture: Entidades, Casos de Uso (Logica de Negocio), Controladores (Brindan acceso [Routing, Middleware])

Controladores -> Services -> Libs

Esta organización de carpetas y archivos hace hermosa la aplicacion

Muy buena explicación del funcionamiento de los servicios.