GraphQL directives as middlewares
yarn add graphql-directives-middlewares
You need to have
graphql
andgraphql-tools
installed on your project.
We create this library because we needed our directives to be sorted at runtime.
We were on the case we needed to execute our @auth
directive BEFORE our @api
directive take place.
With graphql-directives-middlewares
you just have to declare, in your schema, the directives in order you want them to take place.
With this declaration:
type Query {
users: [User] @auth(requires: ADMIN) @api(name: "users")
}
Your @auth
directive will be called BEFORE the @api
one.
If you invert @auth
and @api
, then @api
directive will be called BEFORE the @auth
, without changing your implementation!
createVisitFieldDefinition(name: string, impl: (params, next) -> (...args))
name
: the directive name you use in yourgql
schemaimpl
: is a function that takeparams
andnext
and return a function that is your graphql custom resolverparams
are your directives argumentsnext
is the next resolver to call, like in a middleware engine
createVisitObject(name: string, impl: (params, next) -> (...args))
name
: the directive name you use in yourgql
schemaimpl
: is a function that takeparams
andnext
and return a function that is your graphql custom resolverparams
are your directives argumentsnext
is the next resolver to call, like in a middleware engine
Example with a @auth
directive
- define your directive in the schema
export default `
enum Role {
ADMIN
USER
VIEWER
}
directive @auth(requires: Role = ADMIN) on FIELD_DEFINITION
`
- create your directive implementation
import { GraphQLList } from 'graphql'
import { createVisitFieldDefinition } from 'graphql-directives-middlewares'
export default createVisitFieldDefinition(
'auth',
(params, next) => async (...args) => {
const { requires: requiredRole } = params
if (!requiredRole) {
return next()
}
const [, , context, definition] = args
const { role } = context
if (!role.includes(requiredRole)) {
if (definition.returnType instanceof GraphQLList) return []
throw new Error('Unauthorized')
}
return next()
},
)
- bind your directives implementation to your schema
import { makeExecutableSchema } from 'graphql-tools'
import typeDefs from './types'
import auth from './auth'
export default () => {
const resolvers = {
Query: {
users: () => [
{
id: 'fabien-juif-1',
fullName: 'Fabien JUIF',
},
],
},
}
return makeExecutableSchema({
typeDefs,
resolvers,
schemaDirectives: {
auth,
},
})
}
- use your directives
import { gql } from 'apollo-server'
import user from './user'
export default gql`
${user}
type Query {
users: [User] @auth(requires: ADMIN)
}
`