Graphql-shield: Question: How can we rely on specific fields in rules?

Created on 26 Jul 2018  路  10Comments  路  Source: maticzav/graphql-shield

Hello, first of all thanks for the great lib!

I have a a question. In docs there is a rule defined and used as:

const isOwner = rule()(async (parent, args, ctx, info) => {
  return ctx.user.items.some(id => id === parent.id)
})

const permissions = shield({
  /* ... */
  User: {
    secret: isOwner
  },
})

The rule uses parent.id which is ID of user to determine if the user is a owner. From what I've been able to understand by playing with this library, we can't really rely on any field of parent.

Let's say the client queries something like:

query {
  me {
    id
    secret
  }
}

Where me is a Query which returns current user - then everything is correct. However, what if client queries a Query:

query {
  me {
    # notice the missing ID
    secret
  }
}

then the parent.id in isOwner rule is undefined and we can't correctly decide whether the user is owner or not.

How can the isOwner rule be implemented correctly? Is there even a way to do that?

Thanks!

Most helpful comment

Yes, thank you! For anyone else having the same problem, it should look like this:

const { fragmentReplacements, schema } = applyMiddleware(
  plainSchema,
  permissions,
)

// create transforms array for transformSchema
const transforms = [new ReplaceFieldWithFragment(schema, fragmentReplacements)]
// apply transformations
const finalSchema = transformSchema(schema, transforms)

All 10 comments

Hey @nemcek 馃憢

That's an interesting question, and indeed you are right! The current version doesn't support fragments. Fragments are a way of telling graphql resolver which information your resolver requires to work correctly.

The next version of shield should introduce this with the following syntax. Right now, we fully depend on graphql-tools PR and grpahql-middleware fragments support which should land in no time.

const nestedRule = rule({
  cache: 'strict',
  fragment: 'fragment UserID on User { id }',
})(async ({ id }, args, ctx, info) => {
  return somethingWithID(id)
})

I hope this covers your idea. Tell me if you have any thoughts on this as well! 馃檪

Oh, sweet!

Just to be sure I understood it correctly. In the next version if I use rule with a fragment, for example as:

const isOwner = rule({
    fragment: 'fragment UserID on User { id }',
})(async (parent, args, ctx, info) => {
  return ctx.user.items.some(id => id === parent.id)
})

then I can be sure that parent.id will be populated, right?
If that's the case then shield solves all my use-cases. Love it!

Yeah, that鈥檚 exactly that! Perfect 馃殌馃帀

Is it live? parent is always undefined even when I set cache: 'strict' (the fragment part doesn't even matter):

const isOwner = rule({
  cache: 'strict',
  fragment: 'fragment Id on Item { id }',
})(async (parent, args, ctx: Context, info) => {
  console.log(parent) // undefined
  return true
})

Note: I'm using:

"apollo-server-express": "^2.0.5",
"graphql-middleware": "^1.7.1",
"graphql-shield": "^3.2.0",

Hey @kratam 馃憢

Could you also provide schema and rules? Also, you should note thay you have to use fragmentReplacements returned by middleware; maybe we should indicate that in the docs.

Wow, super fast response! :) How can I pass fragmentReplacements to ApolloServer?

Hey 馃憢

Sorry for the delayed response. fragmentReplacements are part of schema forwarding; graphql-binding supports this using fragmentReplacements parameter.

Besides that, you could also use standalone transforms which are supported by ApolloServer, especially ReplaceFieldWithFragment

https://www.apollographql.com/docs/graphql-tools/schema-transforms.html#Other

I hope this helps you with your problem 馃檪

Yes, thank you! For anyone else having the same problem, it should look like this:

const { fragmentReplacements, schema } = applyMiddleware(
  plainSchema,
  permissions,
)

// create transforms array for transformSchema
const transforms = [new ReplaceFieldWithFragment(schema, fragmentReplacements)]
// apply transformations
const finalSchema = transformSchema(schema, transforms)

What libraries are transformSchema and ReplaceFieldWithFragment from?

I believe transformSchema from graphql-tools is now called wrapSchema

Was this page helpful?
0 / 5 - 0 ratings

Related issues

viztor picture viztor  路  6Comments

creativiii picture creativiii  路  4Comments

nolandg picture nolandg  路  5Comments

dakshshah96 picture dakshshah96  路  5Comments

OGNeutron picture OGNeutron  路  4Comments