Schema Directives
Using schema directives to implement permission checks
[edit]
Directives are a bit like annotations in any other language. They give you a way to specify some behaviour without directly binding to the implementation. This can be really useful for cross cutting concerns like permission checks.
Note: The current directives implementation is still fairly limited, and is designed to cover the most common “field middleware” case.
Directives are declared in your schema, along with all your other types. Lets define a @hasRole directive:
directive @hasRole(role: Role!) on FIELD_DEFINITION
enum Role {
ADMIN
USER
}
When we next run go generate, gqlgen will add this directive to the DirectiveRoot
type DirectiveRoot struct {
HasRole func(ctx context.Context, obj interface{}, next graphql.Resolver, role Role) (res interface{}, err error)
}
The arguments are:
We can call this on any field definition now:
type Mutation {
deleteUser(userID: ID!): Bool @hasRole(role: ADMIN)
}
Finally, we need to implement the directive, and pass it in when starting the server:
package main
func main() {
c := Config{ Resolvers: &resolvers{} }
c.Directives.HasRole = func(ctx context.Context, obj interface{}, next graphql.Resolver, role Role) (interface{}, error) {
if !getCurrentUser(ctx).HasRole(role) {
// block calling the next resolver
return nil, fmt.Errorf("Access denied")
}
// or let it pass through
return next(ctx)
}
http.Handle("/query", handler.GraphQL(todo.NewExecutableSchema(c), ))
log.Fatal(http.ListenAndServe(":8081", nil))
}