Skip to content

Instantly share code, notes, and snippets.

Last active March 16, 2023 23:03
Show Gist options
  • Save JoshCheek/0654a3570b7f3db6e1dfc459488695f1 to your computer and use it in GitHub Desktop.
Save JoshCheek/0654a3570b7f3db6e1dfc459488695f1 to your computer and use it in GitHub Desktop.
Example of how we can prob use resolvers in graphql-go so that you only need to be authenticated if you are asking for fields that require it.
package main
import (
gql ""
// Made up schema: you can ask for a course, and if you are logged in, then you
// can ask if you are enrolled in that course.
var s = `
directive @isAuthenticated on FIELD_DEFINITION
type Query {
course(name: String!): Course
type Course {
name: String!
enrolled: Boolean! @isAuthenticated
type RootResolver struct{}
type QueryResolver struct{ user *User }
type CourseResolver struct {
user *User
requestedCourse string
// you will have to update it with `go get`
// and then you will have to change the directive to look like this,
// and change teh way we pass it in the shema options to match what is in the main fn
type IsAuthenticatedDirective struct{}
func (h *IsAuthenticatedDirective) Resolve(ctx context.Context, args interface{}, next directives.Resolver) (output any, err error) {
_, ok := ctx.Value("currentUser").(*User)
if !ok {
return nil, fmt.Errorf("Unauthorized")
return next.Resolve(ctx, args)
func (h *IsAuthenticatedDirective) ImplementsDirective() string {
return "isAuthenticated"
// users will just have a "set" of courses they're enrolled in
type User struct{ courses map[string]bool }
// This doesn't work for some reason, I want to grab the user from the context in the root resolver
// func (r *RootResolver) Query(ctx context.Context) *QueryResolver {
// user, _ := ctx.Value("currentUser").(*User)
// return &QueryResolver{user: user}
// }
func (r *RootResolver) Query() *QueryResolver {
return &QueryResolver{user: nil}
func (r *QueryResolver) Course(args *struct{ Name string }) *CourseResolver {
return &CourseResolver{user: r.user, requestedCourse: args.Name}
func (r *CourseResolver) Name() string {
return r.requestedCourse
// Really stupid, it has to receive the context b/c the query resolver couldn't
// So now every authenticated field will have to pull the current user out of the
// context instead of being able to do that at the root query object.
func (r *CourseResolver) Enrolled(ctx context.Context) bool {
// _, isEnrolled :=[r.requestedCourse] // <-- what we want to do, we know the user is present b/c this field is authenticated
user := ctx.Value("currentUser").(*User) // <-- what we have to do instead
_, isEnrolled :=[r.requestedCourse]
return isEnrolled
func main() {
opts := []gql.SchemaOpt{
schema := gql.MustParseSchema(s, &RootResolver{}, opts...)
run := func(user *User, query string) {
ctx := context.Background()
if user != nil {
ctx = context.WithValue(ctx, "currentUser", user) // in the app, this comes from a middleware
response := schema.Exec(ctx, query, "", map[string]any{})
json, _ := json.Marshal(response)
fmt.Printf("response: %s\n", json)
u := &User{courses: map[string]bool{"course2": true}}
run(u, `query { course(name: "course1") { name enrolled } }`)
run(u, `query { course(name: "course2") { name enrolled } }`)
run(u, `query { course(name: "course3") { name enrolled } }`)
run(nil, `query { course(name: "course4") { name enrolled } }`)
run(nil, `query { course(name: "course5") { name } }`)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment