github.com/99designs/gqlgen@v0.17.45/docs/content/reference/directives.md (about) 1 --- 2 title: Using schema directives to implement permission checks 3 description: Implementing graphql schema directives in golang for permission checks. 4 linkTitle: Schema Directives 5 menu: { main: { parent: 'reference', weight: 10 } } 6 --- 7 8 Directives act a bit like annotations, decorators, or HTTP middleware. They give you a way to specify some behaviour based on a field or argument in a generic and reusable way. This can be really useful for cross-cutting concerns like permission checks which can be applied broadly across your API. 9 10 **Note**: The current directives implementation is still fairly limited, and is designed to cover the most common "field middleware" case. 11 12 ## Restricting access based on user role 13 14 For example, we might want to restrict which mutations or queries a client can make based on the authenticated user's role: 15 ```graphql 16 type Mutation { 17 deleteUser(userID: ID!): Bool @hasRole(role: ADMIN) 18 } 19 ``` 20 21 ### Declare it in the schema 22 23 Before we can use a directive we must declare it in the schema. Here's how we would define the `@hasRole` directive: 24 25 ```graphql 26 directive @hasRole(role: Role!) on FIELD_DEFINITION 27 28 enum Role { 29 ADMIN 30 USER 31 } 32 ``` 33 34 Next, run `go generate` and gqlgen will add the directive to the DirectiveRoot: 35 ```go 36 type DirectiveRoot struct { 37 HasRole func(ctx context.Context, obj interface{}, next graphql.Resolver, role Role) (res interface{}, err error) 38 } 39 ``` 40 41 The arguments are: 42 - *ctx*: the parent context 43 - *obj*: the object containing the value this was applied to, e.g.: 44 - for field definition directives (`FIELD_DEFINITION`), the object/input object that contains the field 45 - for argument directives (`ARGUMENT_DEFINITION`), a map containing all arguments 46 - *next*: the next directive in the directive chain, or the field resolver. This should be called to get the 47 value of the field/argument/whatever. You can block access to the field by not calling `next(ctx)` 48 after checking whether a user has a required permission, for example. 49 - *...args*: finally, any args defined in the directive schema definition are passed in 50 51 ## Implement the directive 52 53 Now we must implement the directive. The directive function is assigned to the Config object before registering the GraphQL handler. 54 ```go 55 package main 56 57 func main() { 58 c := generated.Config{ Resolvers: &resolvers{} } 59 c.Directives.HasRole = func(ctx context.Context, obj interface{}, next graphql.Resolver, role model.Role) (interface{}, error) { 60 if !getCurrentUser(ctx).HasRole(role) { 61 // block calling the next resolver 62 return nil, fmt.Errorf("Access denied") 63 } 64 65 // or let it pass through 66 return next(ctx) 67 } 68 69 http.Handle("/query", handler.NewDefaultServer(generated.NewExecutableSchema(c), )) 70 log.Fatal(http.ListenAndServe(":8081", nil)) 71 } 72 ``` 73 74 That's it! You can now apply the `@hasRole` directive to any mutation or query in your schema.