1
votes

I am new using graphql and I would like to improve some features at my API, one of the is get a better filter. This API should return some recipe base on the ingredients that the user will inform in the respective APP, This is The resolver I am using:

module.exports = {
  Recipe: {
    async ingredients(recipe, _, { dataSources }) {
      return await dataSources.IngredientService.getRecipeIngredients(recipe.id)
    },
  },
  Query: {
    recipe: async () =>  db('Recipe'),
    ingredient: async () => db('Ingredient'),
    recipeByIngredient:async () => db('Recipe'),
  }}

the service

class ingredientService {
  async getRecipeIngredients(recipe_id) {
      const filteredRecipe = db('Ingredient')
      .where({ recipe_id })
      .join('Recipe', 'Recipe.id', '=', recipe_id)
      .select('Recipe.*', 'Ingredient.*')
      .whereIn('Ingredient.name', ["sal", "açucar"])
      return await filteredRecipe
  }

the query schema

type Query {
  recipe(name:[String]): [Recipe]
  ingredient:[Ingredients]
  recipeByIngredient(ingredients:String):[Ingredients]
}
type Recipe {
  id: Int
  title: String!
  author: String
  link: String
  category: String
  subcategory:String
  ingredients:[Ingredients]
}

type Ingredients{
    id:Int
    name:String
    quantity:Float
    measure:String
    observation:String
  }

The filter is working but I would like ot improve 2 things:

  1. When I see the return no the graphql "playground", when there is no value to the ingredient (that is in on different table from recipes), then the ingredient value is "null" and I would like to not even return the recipe.
  2. I could not make the filter work. I reated the query type "recipe(name:[String]): [Recipe]", for example, but I do not know how to filter it from there. It means, I would like to ingredients filter over my query, filtering the result as expected

quer: recipe(name :["sal", "açucar", "farinha"]){ id title author link category subcategory ingredients{ name quantity measure observation } }

but as you can see, the resolver is hardcode, how could I sent the filter to the resolver?

can anyone help me on it? Thanks a lot.

2
forget about your service ... look at resolvers ... no any filtering there... you have no idea about passing parameters to resolvers (2nd arg) ... [AGAIN!!] search for tutorials!xadm

2 Answers

2
votes

In general, to handle filtering, I set create a Condition type, named based on context. Here, maybe you'd like to pass a type RecipeCondition, which defines fields to effectively filter or scope the recipes returned, for example, based on whether it has ingredients in your datastore. This assumes you may want to add additional conditions in future (otherwise, could just hardcode condition in your sql).


    type RecipeCondition {
      ingredientsRequired: Bool
      
      // Add other condition fields here as needed
      ...
     }


   type Query {
     recipe(name:[String], condition: RecipeCondition): [Recipe]
       ...
    }

I would handle the filter at the top level where you initially fetch recipes with db service (as opposed to handling in ingredients subresolver). You can simply use this condition, accessible on the recipe resolver arg, and pass it to your db service func that initially fetches a recipes array. If the condition ingredientsRequired is true, filter with sql appropriately (will require join to ingredients table and whereIn condition -- if your passing an array of recipe names, this may need to be completed iteratively). In this way, a recipe with no ingredients will not even hit the ingredients subresolver (assuming that condition is desired).

1
votes

Thank you all that tried to help, all these comments was very important to guide to the final answer. I got one posible solution, that I would like to share and get your feedback, if posible.

On first, I improved my Query resolver

  Query: {
    recipe(obj, {name}, {dataSources}, info) {
      if (name) {
        return dataSources.IngredientService.getIngredientsByName(name)
      } else {
        return db('Recipe')  
      }
    }

Second, I changed my Service to receive the filter

 async getIngredientsByName(name) {
    const filteredRecipe = db('Ingredient')
    //.where({ recipe_id })
    .join('Recipe', 'Recipe.id', '=', 'Ingredient.recipe_id')
    .select('Recipe.*', 'Ingredient.name', 'Ingredient.quantity', 'Ingredient.measure','Ingredient.observation')
    .whereIn('Ingredient.name', name)
    return await filteredRecipe

now is everything working fine and making the filter as expected.

Once again, thanks a lot, all of you.