docs/content/reference/directives.md
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.
Note: The current directives implementation is still fairly limited, and is designed to cover the most common "field middleware" case.
For example, we might want to restrict which mutations or queries a client can make based on the authenticated user's role:
type Mutation {
deleteUser(userID: ID!): Bool @hasRole(role: ADMIN)
}
Before we can use a directive we must declare it in the schema. Here's how we would define the @hasRole directive:
directive @hasRole(role: Role!) on FIELD_DEFINITION
enum Role {
ADMIN
USER
}
Next, run go generate and gqlgen will add the 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:
FIELD_DEFINITION), the object/input object that contains the fieldARGUMENT_DEFINITION), a map containing all argumentsnext(ctx)
after checking whether a user has a required permission, for example.Now we must implement the directive. The directive function is assigned to the Config object before registering the GraphQL handler.
package main
import (
"context"
"log"
"net/http"
"github.com/99designs/gqlgen/graphql"
"github.com/99designs/gqlgen/graphql/handler"
"github.com/99designs/gqlgen/graphql/handler/transport"
)
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)
}
srv := handler.New(NewExecutableSchema(c))
srv.AddTransport(transport.POST{})
http.Handle("/query", srv)
log.Fatal(http.ListenAndServe(":8081", nil))
}
That's it! You can now apply the @hasRole directive to any mutation or query in your schema.