No tienes acceso a esta clase

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

Anidamiento dinámico

22/24
Recursos

Aportes 5

Preguntas 1

Ordenar por:

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

Excelente característica, incentiva a la reutilización de consultas simples y reduce el números de consultas complejas que surgen usando inner join en las consultas sql.

si en el resultado final les arroja un error
es porque en el video no se alcanza a ver que el id va dentro y no fuera

  return service.getByCategory(id);

Anidamiento dinámico

Para entender como lograr un anidamiento dinámico, primero tenemos que lograr que cuando le pidamos a GraphQL una categoría específica, nos mande los productos de esta categoría, veamos como:

Añadimos en el esquema la nueva query:

type Query {
  # ...

  # categories
  category(id: ID!): Category
}

type Category {
  # relación entre productos
  product: [Product]
}

Ahora vamos al category.resolvers.js y añadimos la siguiente lógica:

const getCategory = (_, { id }) => {
  return service.findOne(id)
}

module.exports = { 
	// ...
	getCategory
}

Ahora añadimos la función a los resolvers:

const { addCategory, getCategory } = require('./category.resolvers')

const resolvers = {
  Query: {
    // ...
    category: getCategory
  },
  Mutation: {
    // ...
  },
};

module.exports = resolvers

Con esto, si realizamos una consulta solicitando los productos de una categoría, obtendremos los resultados correctamente.

Sin embargo, si observamos los servicios de categoría, notaremos que la relación siempre se carga, incluso si no la solicitamos en la consulta. ¿Cómo podemos solucionar esto?

GraphQL ofrece una herramienta para resolver este problema. Podemos definir un campo como un resolver, lo que nos permite ejecutar código solo cuando ese campo es requerido.

Teniendo esto en cuenta, veamos cómo añadir esta lógica en nuestro servidor:

Primero vamos a nuestro servicio de productos y vamos a crear la función que nos va a enviar estos datos solo cuando los pidamos

class ProductsService {
  // ...
	async getByCategory(id) {
    return await models.Product.findAll({ where: { categoryId: id }});
  }
}

Ahora vamos a nuestro **product.resolver** y vamos a crear lo siguiente:

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

// ...

// el parent es el campo que ignorábamos
// Lo utilizamos cuando ejecutamos campos de forma dinámica
const getProdutsByCategory = (parent) => {
  const id = parent.dataValues.id;
  return service.getByCategory(id)
}

module.exports = {
  // ...
  getProdutsByCategory
}

Ahora vamos al resolver principal a hacer lo siguiente:

const { /* ... */ getProductsByCategory } = require('./product.resolvers')

const resolvers = {
  Query: {
    // ...
  },
  Mutation: {
    // ...
  },
  // Hacemos que el campo 'products' de 'Category' se ejecute como un resolver
  Category: {
    // En el campo 'products', llamamos a la función 'getProductsByCategory'
    products: getProductsByCategory
  }
};

Hay que tener en cuenta que esta consulta solo se ejecuta si enviamos el campo products, de lo contrarío, no lo va a hacer. Con esto no estamos haciendo consultas innecesarias en nuestra base de datos.

Anidamiento dinamico

se crea un type que rotorne otro type que este relacionado en la base de datos

type Category {
  id: ID
  name: String,
  image: String,
  createdAt: String
  products:[Product!]! 
 }
 type Query {
  allCategories: [Category]
  categoryById(id:ID): Category

 }
 type Product {
  id:ID!
  name:String!
  price:Float!
  description:String!
  createdAt:String!
  image:URL!
  category:Category
 }

definimos ese type en nuestros resolvers, por lo tanto el type debe llamarse igual al definido

const { login } = require("./auth/auth.resolver")
const { allCategories, categoryById , addCategory} = require("./categories.resolver")
const { product, products ,createProduct,deleteProduct,updateProduct, getProductByCategory} = require("./product.resolver")
const { getPersons , findPerson, createPerson} = require("./resolver.person")

const {RegularExpression} = require('graphql-scalars')

const CategoryNameType= new RegularExpression('CategoryNameType', /^[a-zA-Z0-9]{3,8}$/);

const resolvers= {
  Query:{
    saludo: ()=> 'hola mundillo de internet',
    persons:getPersons,
    findPerson,
    allCategories,
    categoryById,
    product,
    products
  },
  Mutation:{
    createPerson,
    createProduct,
    deleteProduct,
    updateProduct,
    addCategory,
    login
  },
  CategoryNameType,
  Category: {
    products: getProductByCategory
  }
}

module.exports = resolvers

getProductByCategory hace la peticion a la base de datos

const getProductByCategory =async(root,_)=>{
// root es el objeto padre del cual ha sido llamado el resolver es decir: Category
  return await service.getByCategory(root.dataValues.id)
// creamos un metodo en el servicio que retorne los productos relacionados a la categoria
// por medio del id 
// asi solo el resolver se ejecuta si en la consulta son requeridos los productos
}

metodo del servicio

async getByCategory(idCategory){
    return await models.Product.findAll({where:{categoryId:idCategory}})
  }

Dynamic nesting

It’s possible to modify the output of a property inside a schema type. To do so, we can use the parent param, which is the first parameter passed to a resolver.

We’ll modify the products property from the Category type which is already created in schema.graphql. So we must do this in the resolvers:

export const resolvers = {
  Query: {
    category: getCategory, // the parent param will contain what this resolver returns
  },
  Mutation: {
    // ...
  },
  // The same name as in the schema.graphql (Category)
  Category: {
    products: getProductsOfCategory, // <--- modifying its output
  },
};

We call Category and modify the output of the products property.

<br>

The function getProductsOfCategory it’s declared inside product.resolvers.ts file. This is what it does:

const service = new ProductsService();

export const getProductsOfCategory = (parent: any) => {
  const categoryId = parent.dataValues.id;
  return service.findProductsByCategory(categoryId);
};

So basically we use the parent parameter, which returns the data that is already retrieved by the category query itself. In the parent we can find the categoryId that we’ll use to make another query to the db, this time only retrieving the products from that specific category.