Hello,
I want to develop my server's typeDefs/resolvers in a modular way. In order to do it, I need a way to collocate shield's rules with my resolvers.
I'd like to leverage the fact that we can declare a resolver using an object with the resolve key to add extra informations for my middlewares stack :
const resolvers = {
Mutation: {
createUser: {
// pass rules using a special "permissions" key
permissions: isAuthenticated,
resolve: forwardTo('db')
}
}
}
I already do this for my validation layer (big props to @JCMais for showing the way with his awesome post 馃憦 ) and it's working great so far :
const resolvers = {
Mutation: {
createUser: {
// pass rules using a special "permissions" key
permissions: isAuthenticated,
// pass validation rules to my yup-middleware
validation: (_, __, { db }) =>
update('data.username', f =>
f.unique(async username => !(await db.exists.User({ username })))
)
resolve: forwardTo('db')
}
}
}
@maticzav gave me hints about collecting all the rules from the schema using something like https://github.com/graphql-binding/graphql-binding/blob/master/src/fragmentReplacements.ts
Here you can still declare shield's the current way by passing a map of your rules, but you can also overwrite them or define new ones using a special key inside your resolvers definitions (I pick 'permissions' by default, but it could be 'shield' as well, you can pick your own!)
const _ = require('lodash/fp')
const graphqlShield = require('graphql-shield')
const { middleware } = require('graphql-middleware')
function shield(initialPermissions = {}, options = {}) {
const { permissionsKey = 'permissions', ...shieldOptions } = options
return middleware(schema => {
const permissions = _.merge(
initialPermissions,
collectPermissions(schema, permissionsKey)
)
return graphqlShield.shield(permissions, shieldOptions).generate(schema)
})
}
function collectPermissions(schema, permissionsKey) {
const resolvers = {
Query: schema.getQueryType().getFields(),
Mutation: schema.getMutationType().getFields()
}
let permissions = {}
for (const typeName in resolvers) {
const fieldResolvers = resolvers[typeName]
for (const fieldName in fieldResolvers) {
const fieldResolver = fieldResolvers[fieldName]
if (fieldResolver[permissionsKey]) {
permissions = _.set(
[typeName, fieldName],
fieldResolver[permissionsKey],
permissions
)
}
}
}
return permissions
}
module.exports = {
...graphqlShield,
shield
}
Hey @jgoux 馃憢,
I think yours is a fantastic idea! Being able to define rules next to your resolvers could open up a whole new set of possibilities. I believe this idea would best be best implemented as a side package, primarily to keep shield as unopinionated as possible - in a structure non-interrupting manner.
Furthermore, what I am curious about is how would you suggest the implementation of type- or schema-wide rules. These are rules that are applied to every field of a particular type or, as in the second case, to every single field in a schema?
Let me know what you think! 馃檪
how would you suggest the implementation of type- or schema-wide rules
I thought about type-wide rules and I didn't find a proper solution for them yet. We can't really declare them alongside the resolvers (at least using the regular syntax) because it's not a valid GraphQL typedef. For now you can still declare them in the centralized config object.
For the schema-wide rules, let's just keep them in the centralized configuration as they are global per se. 馃槃
I'll publish the current implementation as a side-package then. Do you have any idea for the name? It will be the exact same API as shield, with the additional permissionsKey in the option object.
Btw, about this topic, I've created a RFC on graphql-js for having a supported way to declare this extra metadata directly on each field definition: https://github.com/graphql/graphql-js/issues/1527
Would love some opinions there
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.
The feature has been merge in graphql-js and graphql-tools recently, maybe a guide could be implemented here?
Most helpful comment
Btw, about this topic, I've created a RFC on
graphql-jsfor having a supported way to declare this extra metadata directly on each field definition: https://github.com/graphql/graphql-js/issues/1527Would love some opinions there