[Feature request] Allow decentralizing the definition of the rules
See original GitHub issueHello,
Feature request
Is your feature request related to a problem? Please describe
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.
Describe the solution you’d like
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')
}
}
}
Additional context
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')
}
}
}
Implementation
@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
}
Issue Analytics
- State:
- Created 5 years ago
- Reactions:5
- Comments:5 (1 by maintainers)
Top GitHub Comments
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/1527Would love some opinions there
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.