graphql-go icon indicating copy to clipboard operation
graphql-go copied to clipboard

How to use viewer in context

Open saviodo5591 opened this issue 7 years ago • 17 comments

I'm a little confused as to how I can implement the viewer methodology with this. I am looking to use this viewer method so that I will only use the queries that are in accordance to the relative user.

saviodo5591 avatar Sep 23 '17 07:09 saviodo5591

So you graphql schema might look like this

type Query {
  viewer: Viewer
}

type Viewer {
  id: ID!
  name: String!
  mail: String!
  favourites: [Favourite]!
}

This would mean that viewer information will be relative to who is logged in, then you need to check for a token, cookie, or whatever method you got for it.

In my case i'm using json web tokens (jwt) so it might look like this


type viewerResolver struct {
	user   User
}

func (r *Resolver) Viewer(ctx context.Context) (*viewerResolver, error) {
	var userID *string = nil

	token := ctx.Value("jwt").(*jwt.Token)
	if token != nil {
		claims, _ := token.Claims.(jwt.MapClaims)
		id := claims["sub"].(string)
		userID = &id
	}

       user := getUser(userID)

	return &viewerResolver{
		user: user,
	}, nil
}

I'm using this middleware function to get the token out of the http request header Authorization and pass it down by context, which my viewer resolver above has access to it.

func auth(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		ctx := r.Context()
		token := r.Header.Get("Authorization")
		jwt, err := checkToken(token)
		if err != nil {
			fmt.Println(err)
		}

		next.ServeHTTP(w, r.WithContext(context.WithValue(ctx, "jwt", jwt)))
	})
}

And there is it, there you can get all the information relative to your user, if there is anything else you might need or have a question please let me know

lpalmes avatar Sep 23 '17 22:09 lpalmes

@lpalmes Awesome! I will definitely try this out! I'll let you know if I have any more questions

saviodo5591 avatar Sep 23 '17 22:09 saviodo5591

@lpalmes What jwt library do you happen to be using?

saviodo5591 avatar Sep 23 '17 23:09 saviodo5591

Hey @saviodo5591, this one https://github.com/dgrijalva/jwt-go

lpalmes avatar Sep 24 '17 03:09 lpalmes

I was running into a few issues with the claims option not showing up. Turns out I was using the standard "Context" library vs the "golang.org/x/net/context" package. The only other thing I'm a tad confused about is the getUser(userID) method, is this a resolver function or is it a database call? Additionally, the checkToken(token) seems to be throwing an unresolved reference. I'm probably missing that's really obvious but I can't seem to be getting it to work.

saviodo5591 avatar Sep 25 '17 07:09 saviodo5591

getUser and checkToken were private functions just to demonstrate the point. If you will I could make an example with everything in form of a repo or gist

lpalmes avatar Sep 25 '17 12:09 lpalmes

That would actually be great! I tend to work better with actually seeing the code sometimes

saviodo5591 avatar Sep 25 '17 12:09 saviodo5591

Then I will create an example and let you know as soon as I’m able to. I’ll let you know in a couple of hours

lpalmes avatar Sep 25 '17 12:09 lpalmes

Ok great thank you very much!

saviodo5591 avatar Sep 25 '17 12:09 saviodo5591

Hey @saviodo5591, here you go, a working example, everything is in the main.go file and the schema is on another file, it took longer than expected because i added users, login, and some extra stuff.

Don't mind the repo, is a graphql-go introduction i was writing but is on hold till the api comes along, again, hope it serves you well.

lpalmes avatar Sep 26 '17 00:09 lpalmes

hi lpalmes , very beautiful example. thanx a lot. I was looking for some information on context.

JitinGuglani avatar Nov 11 '17 09:11 JitinGuglani

@lpalmes Thanks for the example. First I did go get . and I ran with go run main.go. I got EOF as the output when I navigated to http://localhost:8080/graphql

smithaitufe avatar Nov 18 '17 00:11 smithaitufe

Hey @smithaitufe, graphiql (the ui to graphql) is running at http://localhost:8080, and the route for sending the graphql queries is at /graphql (Plain http post requests with the query) as seen in this line.

Glad you like it, and hope i'm begin useful, if you got any more questions just ask, drop an issue, anything and i do my best to help.

lpalmes avatar Nov 18 '17 22:11 lpalmes

@lpalmes I checked again and it is working perfectly fine. The issue was my poor internet connection. Thanks so much. With time you must consider how to handle uploading of files, database access with the use of context etc. I can easily do that with Nodejs but I am considering porting my ecommerce backend to go

smithaitufe avatar Nov 19 '17 01:11 smithaitufe

Hi, How can Resolvers pass Contexts to sub-Resolvers? I would like to set a value in my root queries available for sub-resolves, this would also be a crucial path towards batching request. Thanks, Dan.

djmcgreal avatar Nov 24 '17 11:11 djmcgreal

Looks like the Tracer is for this purpose? Is there an example?

djmcgreal avatar Nov 24 '17 12:11 djmcgreal

Maybe someone not so new to Go would have found this easier, but I ended up doing something like...

import trace "github.com/neelance/graphql-go/trace"

type ourTracer struct {
	delegate trace.OpenTracingTracer
}

func (t ourTracer) TraceQuery(ctx context.Context, queryString string, operationName string, variables map[string]interface{}, varTypes map[string]*introspection.Type) (context.Context, trace.TraceQueryFinishFunc) {
//Stuff
	return t.delegate.TraceQuery(ctx, queryString, operationName, variables, varTypes)
}

func (t ourTracer) TraceField(ctx context.Context, label, typeName, fieldName string, trivial bool, args map[string]interface{}) (context.Context, trace.TraceFieldFinishFunc) {
//Stuff
	return t.delegate.TraceField(ctx, label, typeName, fieldName, trivial, args)
}

djmcgreal avatar Nov 24 '17 15:11 djmcgreal