Puedes crear los servicios usando el generador de Nest CLI con nest g s services/products --flat
Introducción a NestJS
¿Qué es NestJS?
Crea tu primer proyecto con NestJS
Estructura de aplicaciones en NestJS
Presentación del proyecto: Platzi Store
Repaso a TypeScript: tipos y POO
REST API
Introducción a controladores
GET: cómo recibir parámetros
GET: parámetros query
Separación de responsabilidades
Instalación de Postman o Insomnia
Qué es el método POST
Métodos PUT y DELETE para editar y eliminar
Códigos de estado o HTTP response status codes
Integridad de datos
Introducción a servicios: crea tu primer servicio
Implementando servicios en tu controlador
Manejo de errores con throw y NotFoundException
Introducción a pipes: usa tu primer pipe
Crea tu propio pipe
Creando Data Transfers Objects
Validando parámetros con class-validator y mapped-types
Cómo evitar parámetros incorrectos
Próximos pasos
Reto: controladores y servicios restantes
Continúa con el Curso de NestJS: Programación Modular
No tienes acceso a esta clase
¡Continúa aprendiendo! Únete y comienza a potenciar tu carrera
Los servicios en NestJS son los que suelen tener la lógica del negocio y la conexión con la base de datos.
Los servicios son una pieza esencial de las aplicaciones realizadas con el framework NestJS. Están pensados para proporcionar una capa de acceso a los datos que necesitan las aplicaciones para funcionar.
Un servicio tiene la responsabilidad de gestionar el trabajo con los datos de la aplicación, de modo que realiza las operaciones para obtener esos datos, modificarlos, etc.
Para crear un servicio puedes utilizar el comando nest generate service <service-name>
o en su forma corta nest g s <service-name>
.
// app.service.ts
import { Injectable } from '@nestjs/common';
@Injectable()
export class AppService {
getHello(): string {
return 'Hello World!';
}
}
Los servicios utilizan el decorador @Injectable()
y deben ser importados en los providers del módulo al que pertenecen o tu aplicación no lo reconocerá y tendrás errores al levantar el servidor.
// app.module.ts
import { Module } from '@nestjs/common';
import { AppService } from './app.service';
@Module({
imports: [],
providers: [
// Imports de Servicios
AppService
],
})
export class AppModule {}
Crea un método en el servicio para cada propósito que necesites. Uno para obtener un producto, otro para obtener un listado de productos. Uno para crear producto, para actualizar, eliminar, etc.
// src/entities/product.entity.ts
export class Product {
id: number;
name: string;
description: string;
price: number;
stock: number;
image: string;
}
nest g s services/products --flat
// src/services/products.service.ts
import { Injectable } from '@nestjs/common';
import { Product } from './../entities/product.entity';
@Injectable()
export class ProductsService {
private counterId = 1;
private products: Product[] = [
{
id: 1,
name: 'Product 1',
description: 'bla bla',
price: 122,
image: '',
stock: 12,
},
];
findAll() {
return this.products;
}
findOne(id: number) {
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;
}
}
// src/app.module.ts
import { Module } from '@nestjs/common';
...
import { ProductsService } from './services/products.service';
@Module({
imports: [],
controllers: [...],
providers: [AppService, ProductsService], // 👈 New Service
})
export class AppModule {}
Contribución creada con los aportes de: Kevin Fiorentino y Christian Moreno.
Aportes 53
Preguntas 17
Puedes crear los servicios usando el generador de Nest CLI con nest g s services/products --flat
Bien dejo los metodos que arme para el reto, si alguno tiene algo para comparar o mejorar lo vemos!
update(id: number, payload: any) {
const productFound = this.products.findIndex((item) => item.id === id);
let message = '';
if (productFound > 0) {
this.products[productFound] = {
id: id,
...payload,
};
message = 'Product updated';
} else {
message = 'Product not found';
}
return message;
}
delete(id: number) {
const productFound = this.products.findIndex((item) => item.id === id);
let message = '';
if (productFound > 0) {
this.products.splice(productFound, 1);
message = 'product deleted';
} else {
message = 'product not found';
}
return message;
}
En TypeScript también podemos definir que una propiedad de nuestra entidad sea opcional poniendo un signo de interrogación después del nombre de la propiedad:
export class Product {
id: number;
name: string;
description: string;
price: number;
stock?: number;
image?: string;
}
Me parece interesante esta funcionalidad del lenguaje y así tenemos más opciones a la hora de crear entidades
Asi es una de las formas en las que se puede solucionar estos dos metodos 😄
update(payload: any, id: number) {
const product = this.findOne(id)
for (let key in payload) {
if (key !== 'id') {
product[key] = payload[key]
}
}
return product
}
delete(id: number) {
const product = this.findOne(id)
const productIndex = this.products.indexOf(product)
this.products.splice(productIndex, 1)
return product
}
En mi caso me gusta manejar este tipos de ID con una librería: nanoid
npm i nanoid
import { nanoid } from 'nanoid';
const newProduct = {
id: nanoid(4), // 4: longitud de caracteres de id
...payload,
};
Los servicios son una pieza esencial de las aplicaciones realizadas con el framework NestJS
<aside>
Están pensados para proporcionar una capa de acceso a los datos que necesitan las aplicaciones para funcionar.
</aside>
Un servicio tiene la responsabilidad de gestionar el trabajo con los datos de la aplicación, de modo que realiza las operaciones para obtener esos datos, modificarlos, etc.
Con los servicios podemos:
Para construir un servicio podemos usar el CLI de Nest. Para crear la clase de un servicio lanzamos el siguiente comando:
nest generate service products
nest g s products
Además también realiza automáticamente la modificación del archivo app.module.ts
en el que se introdujo
@Injectable
El decorador @injectable
permite inyectarse en los controladores, todo servicio debe tener este decorador antes de la declaración de la clase que lo implementa para poder usar la inyección de dependencias:
import { Injectable } from '@nestjs/common';
@Injectable()
export class ProductsService {}
Siguiendo con el esquema que hice en la clase de los controlares, así creo mis servicios:
nest g s modules/brand/services/brand --flat
nest g s modules/category/services/category --flat
nest g s modules/customer/services/customer --flat
nest g s modules/order/services/order --flat
nest g s modules/products/services/products --flat
nest g s modules/user/services/user --flat
buen momento para usar las interfaces creo
Estos son mis métodos para actualizar y borrar productos en memoria:
update(id: number, changes: any) {
const index = this.products.findIndex((item) => item.id === id);
const product = this.products[index];
this.products[index] = {
...product,
...changes,
};
}
delete(id: number) {
const index = this.products.findIndex((item) => item.id === id);
this.products.splice(index, 1);
return { id };
}
Ok…esto es Angular…que bien!!!
Yo normalmente utilizo interfaces para representar una entidad, no clases.
Sí hay una forma de generar entidades por el cli de nest.
nest generate class nombre-de-la-clase
Abreviado
nest g cl nombre
Y te generá un archivo de pruebas
Hola aqui te dejo mi solución al reto:
update(id: number, payload: any) {
const productFound = this.findOne(id);
let message = '';
if (productFound) {
this.products[productFound.id - 1] = {
...productFound,
...payload,
};
message = 'Product updated';
} else {
message = 'Product not found';
}
return message;
}
delete(id: number) {
const productFound = this.findOne(id);
let message = '';
if (productFound) {
this.products.splice(productFound.id - 1, 1);
message = 'product deleted';
} else {
message = 'product not found';
}
return message;
}
update(id: number, payload: any) {
const productIndex = this.products.findIndex((item) => item.id === id);
if (productIndex !== -1) {
this.products[productIndex] = {
...this.products[productIndex],
...payload,
};
return this.products[productIndex];
}
return null;
}
delete(id: number) {
const productIndex = this.products.findIndex((item) => item.id === id);
if (productIndex !== -1) {
const deletedProduct = this.products.splice(productIndex, 1);
return deletedProduct[0];
}
return null;
}
update(id, payload) {
const product = this.products.find(product => product.id === id)
if(!product) { return }
Object.assign(product, payload)
return product
}
delete(id) {
const length = this.products.length
this.products = this.products.filter(product => (
product.id !== id
))
return length !== this.products.length
? 'Borrado exitosamente'
: 'No existe el producto a eliminar'
Esta es otra forma de hacerlo!
Actualmente creo que es mejor usar una interfaz para hacer el tipado
export interface Product {
id: number;
name: string;
description: string;
price: number;
}
Para el metodo update usando Utility Types y excepciones de NestJs
Controller:
@Put(':id')
update(@Param('id') id: number, @Body() payload: Partial<Product>) {
try {
return this._productService.update(+id, payload);
} catch (e) {
throw new NotFoundException(e.message);
}
}
Service:
update(id: number, payload: Partial<Product>) {
const idPrd = this.products.findIndex((item) => item.id === id);
if (idPrd < 0) throw new Error('Item not Found');
this.products[idPrd] = {
...this.products[idPrd],
...payload,
id: this.products[idPrd].id,
};
return this.products[idPrd];
}
update(payload: Product) {
const index = this.products.findIndex((item) => item.id === payload.id);
this.products[index] = payload;
return payload;
}
delete(id: number) {
const index = this.products.findIndex((item) => item.id === id);
const product = this.products[index];
this.products = this.products.splice(index, 1);
return product;
}
}
Mi solucion al reto:
update(id: number, payload: any) {
const prod = this.products.findIndex(product => product.id === id)
if (!prod) {
throw new Error('Product not found')
}
const updateProduct = this.products[prod]
this.products[prod] = {
...updateProduct,
...payload
}
return this.products[prod]
}
delete(id: number) {
const index = this.products.findIndex(product => product.id === id)
if (index === -1) {
throw new Error('Product not found')
} else {
this.products.splice(index, 1)
return {
message: 'Product deleted'
}
}
}
update(id: number, payload: any){
const updateProduct = {id, ...payload};
return this.products.map((item) => (item.id === id) ? updateProduct : item);
}
delete(id : number){
return this.products.filter((item) => item.id !== id);
}
Una interfaz es un contrato firma que se deberá implementar como estructura.
.
En TS es muy común ver interfaces
como definiciones de tipos personalizados. En contexto es correcto pero genera un debate alrededor de interfaces vs types vs class
.
.
Cuando se define una estructura de bajo nivel como Product
o Tag
, por decir ejemplos, se ven sujetos a la necesidad de clases que permitan su transporte, ya sea como DAO (Data-Access-Object) o DTO (Data-Transfer-Object).
.
✨ Concepto clave
Una interfaz tiene por convención de nombre unaI
como definición, por lo que una interfaz seríaIProduct
.
.
Las interfaces se uitilizan para:
.
Por lo que, definiendo una interfaz como abstracción sería:
interface IProduct {
name: string
catalog: string
serialNumber: () => string
}
Donde al ser implementada:
class Movie implements IProduct {
private name: string
private catalog: string
constructor(name: string, catalog: string) {
this.name = name;
this.catalog = catalog;
}
serialNumber(): string {
return `${name}-${catalog}`
}
}
.
Cuando tenemos la necesidad de emplear un tipado en parámetros, por ejemplo, se puede definir un alias a la interfaz o personalización según su uso:
interface IProduct {
name: string
catalog: string
}
type Product = IProduct // Object interface
type Query = <Pick, 'name'> // Query param
Pueden hacerlo tambien con interfaces:
export interface Product {
id:number,
name:string,
description:string,
price:number,
stock:number,
image:string
}
delete(id: number) {
const find = this.findOne(id).id - 1;
this.products.splice(find, 1);
return { message: `El producto ${id} se elimino correctamente` };
}
update(id: number, payload: any) {
const find = this.findOne(id);
return { ...find, ...payload };
}
Creo que esto podria funcionar
update(payload: Product){
this.products.map(element => {
if(element.id === payload.id){
return payload;
}
return element;
})
}
delete(id: number){
this.products = this.products.filter(item => item.id !== id);
}
public findAll() {
return this.products;
}
public findOne(idProduct: number) {
this.products.find((product) => product.id === idProduct);
}
public create(payload: any) {
const product: Product = {
id: this.counterId,
...payload,
};
this.products.push(product);
this.counterId++;
}
public update(id: number, payload: any) {
let productFound = this.products.find((product) => product.id === id);
if (productFound) {
productFound = {
id: id,
...payload,
};
return true;
}
return false;
}
public delete(id: number) {
const productsFound = this.products.filter((product) => product.id !== id);
if (productsFound.length != this.products.length) {
this.products = productsFound;
return true;
}
return false;
}
edit(id: number, payload: any) {
const productEdit = this.products.findIndex((p) => p.id === id);
this.products[productEdit] = {
id: id,
...payload,
};
}
delete(id: number) {
const productDelete = this.products.findIndex((p) => p.id === id);
this.products.splice(productDelete, 1);
}
update(id: number, payload: any) {
const indexResult = this.products.findIndex((item) => item.id === id);
if (indexResult) {
const updatedItem = {
...this.products[indexResult],
...payload,
};
this.products[indexResult] = updatedItem;
} else {
throw new Error('There is not item into products');
}
}
remove(id : number) {
this.products = this.products.filter((item) => item.id != id);
}
el updateOne y deleteOne creado por mi github copilot xd:
updateOne(id: number, product: ProductEntity): ProductEntity
{
const index = this.products.findIndex((p) => p.id === id);
this.products[index] = product;
return product;
}
deleteOne(id: number): ProductEntity {
const index = this.products.findIndex((p) => p.id === id);
const product = this.products[index];
this.products.splice(index, 1);
return product;
}
A mi particularmente me gusta más declarar los modelos (esquemas de una estructura de datos sin métodos) con “type” así quedaría el typo producto:
export type Product = {
id: number;
name: string;
description: string;
price: number;
stock: number;
image: string;
};
esta es mi implementacion
private productExists(id: number) {
return this.products.some((product) => product.id == id);
}
update(payload: any, id: number) {
if (this.productExists(id)) {
this.products = this.products.map((product) => {
if (product.id == id) {
return { ...payload };
}
return product;
});
} else {
return 'Product not found';
}
}
delete(payload: any) {
if (this.productExists(payload)) {
this.products = this.products.filter((product) => product.id !== payload);
return 'Product was deleted successfully';
} else {
return 'Product not found';
}
}
Hola dejo mi sulucion, cualquier retro es bienvenida
update(id: number, payload: Product) {
let updatedProduct: Product;
this.products = this.products.map<Product>((product) => {
if (id === product.id) {
updatedProduct = { ...product, ...payload };
return updatedProduct;
}
return product;
});
return updatedProduct;
}
delete(id: number) {
this.products = this.products.filter((product) => product.id !== id);
}
Aquí dejo mi propuesta para los métodos.
update(id: number, payload: any) {
const index = this.products.findIndex((p) => p.id === id);
if(index === -1) throw Error('Not found')
this.products[index] = { id, ...payload };
}
delete(id: number) {
this.products = this.products.filter((p) => p.id !== id);
}
This is my service
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) {
const updateProduct = Object.assign(product, payload);
this.products = this.products.filter((r) => r.id !== id);
this.products.push(updateProduct);
return updateProduct;
}
return false;
}
delete(id: number) {
this.products = this.products.filter((r) => r.id !== id);
return id;
}
}
import { Injectable } from '@nestjs/common';
import { IProduct } from 'src/interfaces/product.interface';
@Injectable()
export class ProductsService {
private lastId = 0;
private products: IProduct[] = [
{
id: 1,
name: 'product 1',
price: 5,
},
];
findAll(): IProduct[] {
return this.products;
}
findOne(id: number): IProduct {
const product = this.products.find((item: IProduct) => item.id === id);
return product;
}
create(payload: IProduct) {
this.lastId++;
const newProduct: IProduct = {
id: this.lastId,
...payload,
};
this.products.push(newProduct);
return newProduct;
}
update(id: number, changes: IProduct) {
const index = this.products.findIndex((item: IProduct) => item.id === id);
const product = this.products[index];
const newData = {
...product,
...changes,
};
this.products.splice(index, 1, newData);
return {
message: 'Product updated',
newData,
};
}
delete(id: number) {
const index = this.products.findIndex((item: IProduct) => item.id === id);
this.products.splice(index, 1);
return {
message: 'Product deleted',
};
}
}
parecido al aporte de los compañeros
upgrade(id: number, payload: any) {
const i = this.product.findIndex((item) => item.id === id);
let message = '';
if (i !== -1) {
this.product[i] = {
id: id,
...payload,
};
message = 'Product updated';
} else message = 'Product not found';
return message;
}
delete(id: number) {
const i = this.product.findIndex((item) => item.id === id);
let message = '';
if (i !== -1) {
this.product.splice(i, 1);
message = 'Product delete';
} else message = 'Product no delete';
return message;
}
Crear o registrar nuevo producto
Metodo para retornar o buscar un solo producto
Metodo para retornar o buscar todos los productos
Crear Entidades
crear un servicio en memoria para administrarlo
Los servicios tendrán decorador especial @Injectable
Arquitectura de Nest JS
Van mis métodos.
update(id: number, payload: any) {
const targetIndex = this.products.findIndex((item) => item.id === id);
if (targetIndex > 1) {
this.products[targetIndex] = {
...this.products[targetIndex],
...payload,
};
}
}
delete(id: number) {
this.products = this.products.filter((product) => product.id !== id);
return {
deleted: id,
};
Así quedaron mis métodos
update(id: number, payload: any) {
const productArray = this.products.findIndex((item) => item.id === id);
if (productArray >= 0) {
this.products[productArray] = {
id,
...payload,
};
}
return this.products[productArray];
}
delete(id: number) {
const productArray = this.products.findIndex((item) => item.id === id);
return this.products.splice(productArray, 1);
}
Mi solucion:
update(id: number, payload: any) {
const found = this.products.findIndex((item) => item.id === id);
if (found === -1) throw new Error('Product not found');
this.products[found] = {
id: id,
...payload,
};
return {
Message: 'Product updated',
Updated: this.products[found],
};
}
delete(id: number) {
const found = this.products.findIndex((item) => item.id === id);
if (found === -1) throw new Error('Product not found');
this.products.splice(found, 1);
return {
Message: 'Product deleted',
};
}
Solución a la Tarea:
Me pareció la mejor forma. tal vez si pueda optimizarse más quedo al pendiente de sus comentarios.
update(id: number, payload: Product) {
this.products = this.products.map(product => {
if ( product.id === id ) {
const updatedProduct: Product = {
...product,
...payload
};
return updatedProduct;
}
return product;
});
}
delete(id: number) {
this.products =
this.products.filter(product => product.id !== id);
}
¿Quieres ver más aportes, preguntas y respuestas de la comunidad?