graphql-shield
graphql-shield copied to clipboard
Documents are not populated when running `graphql-shield`, thus leading to `undefined` permission errors.
Question about GraphQL Shield
I am using mongoose
with graphql
via graphql-compose-mongoose
with a schema that has nested relational documents. To populate the document for a query I have provided relation methods via addRelation
, which works in the absence of graphql-shield
permissions, but encounters an _id
undefined
error when using permissions - presumably because the graphql-shield
middleware runs before the documents can be populated?
I have provided a minimal working example below. My question is how can I get this to work?
import { Schema, Document, model, Types } from 'mongoose';
import { composeMongoose } from 'graphql-compose-mongoose';
import { SchemaComposer } from 'graphql-compose';
import { applyMiddleware } from 'graphql-middleware';
import { shield, allow } from 'graphql-shield';
// Mongoose models
export interface Ingredient extends Document {
name: string;
}
const ingredientSchema = new Schema<Ingredient>({
name: { type: String, required: true, unique: true },
});
export interface RecipeIngredient extends Document {
ingredient: Types.ObjectId;
quantity: number;
}
const recipeIngredientSchema = new Schema<RecipeIngredient>({
ingredient: { type: Schema.Types.ObjectId, refPath: 'Ingredient', required: true },
quantity: { type: Number, required: true },
});
export interface Recipe extends Document {
title: string;
ingredients: RecipeIngredient[];
}
const recipeSchema = new Schema<Recipe>({
title: { type: String, required: true },
ingredients: {
type: [{ type: recipeIngredientSchema }],
required: true,
},
});
export const Ingredient = model<Ingredient>('Ingredient', ingredientSchema);
export const IngredientTC = composeMongoose(Ingredient);
export const RecipeIngredient = model<RecipeIngredient>('RecipeIngredient', recipeIngredientSchema);
export const RecipeIngredientTC = composeMongoose(RecipeIngredient);
export const Recipe = model<Recipe>('Recipe', recipeSchema);
export const RecipeTC = composeMongoose(Recipe);
RecipeIngredientTC.addRelation('ingredient', {
resolver: () => IngredientTC.mongooseResolvers.findById(),
prepareArgs: {
_id: (source) => source.ingredient._id,
},
projection: { ingredient: true },
});
// GraphQL resolvers
export const IngredientQuery = {
ingredientById: IngredientTC.mongooseResolvers.findById(),
ingredientByIds: IngredientTC.mongooseResolvers.findByIds(),
ingredientOne: IngredientTC.mongooseResolvers.findOne(),
ingredientMany: IngredientTC.mongooseResolvers.findMany(),
};
export const RecipeQuery = {
recipeById: RecipeTC.mongooseResolvers.findById(),
recipeByIds: RecipeTC.mongooseResolvers.findByIds(),
recipeOne: RecipeTC.mongooseResolvers.findOne(),
recipeMany: RecipeTC.mongooseResolvers.findMany(),
};
// GraphQL shield
export const permissions = shield({ Query: { recipeById: allow } }, { allowExternalErrors: true });
const schemaComposer = new SchemaComposer();
schemaComposer.Query.addFields({
...IngredientQuery,
...RecipeQuery,
});
export const schema = applyMiddleware(schemaComposer.buildSchema(), permissions);
"""GraphQL Query"""
query ExampleQuery($recipeId: MongoID!) {
recipeById(_id: $recipeId) {
_id
ingredients {
_id
quantity
ingredient {
_id
name
}
}
}
}
Here is the output for said query:
{
"data": {
"recipeById": {
"_id": "655bf525063f734161299e59",
"ingredients": [
{
"_id": "655bf525063f734161299e5a",
"quantity": 1,
"ingredient": null
}
]
}
},
"errors": [
{
"message": "Cannot read properties of undefined (reading '_id')",
"locations": [
{
"line": 7,
"column": 7
}
],
"path": [
"recipeById",
"ingredients",
0,
"ingredient"
],
"extensions": {
"code": "INTERNAL_SERVER_ERROR",
"stacktrace": [
"TypeError: Cannot read properties of undefined (reading '_id')",
" at _id (file:///graphql-shield-example/dist/schema.js:29:44)",
" at graphql-shield-example/node_modules/graphql-compose/lib/ObjectTypeComposer.js:1076:36",
" at Array.forEach (<anonymous>)",
" at resolve (graphql-shield-example/node_modules/graphql-compose/lib/ObjectTypeComposer.js:1075:25)",
" at file:///graphql-shield-example/node_modules/graphql-middleware/dist/applicator.mjs:5:112",
" at middleware (file:///graphql-shield-example/node_modules/graphql-shield/esm/generator.js:27:30)",
" at process.processTicksAndRejections (node:internal/process/task_queues:95:5)"
]
}
},
]
}
- [x] I have checked other questions and found none that matches mine.