master
master v0.17.45 v0.17.44 v0.17.43 v0.17.42 v0.17.41 v0.17.40 v0.17.39 v0.17.38 v0.17.37 v0.17.36 v0.17.35 v0.17.34 v0.17.33 v0.17.32 v0.17.31 v0.17.30 v0.17.29 v0.17.28 v0.17.27 v0.17.26

Schema Directives

Using schema directives to implement permission checks
[edit]
You are looking at the docs for the unreleased master branch. The latest version is v0.17.45.

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.

Restricting access based on user role

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)
}

Declare it in the schema

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:

Implement the directive

Now we must implement the directive. The directive function is assigned to the Config object before registering the GraphQL handler.

package main

func main() {
	c := generated.Config{ Resolvers: &resolvers{} }
	c.Directives.HasRole = func(ctx context.Context, obj interface{}, next graphql.Resolver, role model.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.NewDefaultServer(generated.NewExecutableSchema(c), ))
	log.Fatal(http.ListenAndServe(":8081", nil))
}

That’s it! You can now apply the @hasRole directive to any mutation or query in your schema.