gqlgen icon indicating copy to clipboard operation
gqlgen copied to clipboard

How to access the arguments of a query within the RequestContext Object ?

Open dtrckd opened this issue 4 years ago • 11 comments

What happened?

Hi there, When trying to obtain the variables from a request context Object (with graphql.GetRequestContext(ctx).Variables with the following query:

query MyQuery {
  queryNode(filter:{isRoot:true}) {
  name
  }
}

I obtain a empty map.

What did you expect?

map[filter: map[isRoot:true]]

Furthermore, I get the desired result if I use

query MyQuery($args: MyArgs!) {
  queryNode(filter:$args) {
  name
  }
}

{ "args": {"isRoot": true}}

versions

  • glqgen : v0.11.3
  • go: 1.14.1
  • gqlparser: v2/v2.0.1

dtrckd avatar Apr 11 '20 15:04 dtrckd

Hop, I apology, I think I misinterpreted the ctx.Variables object semantic, and thus, in the example above, there is nothing abnormal when this field is empty because no variables (graphql data) was provided.

Thus, allows me to rephrase my issue as follow:

Is it possible to access the arguments of a query within the RequestContext Object ?

dtrckd avatar Apr 11 '20 17:04 dtrckd

Is it possible to access the arguments of a query within the RequestContext Object ?

@dtrckd I did not do it from RequestContext, instead of that from the 2nd input argument (in below example is input)

func (r *mutationResolver) CreateContact(ctx context.Context, input model.NewContact) (*model.Contact, error) {
input NewContact {
  firstName: String!
  lastName: String!
  accountId: String!
}

type Mutation {
  createContact(input: NewContact!): Contact!
}

isuhendro avatar Apr 18 '20 04:04 isuhendro

Tnaks @isuhendro. Yes I can access the input argument from the resolvers but the point is that I need to reconstruct th query as a bytes/string to re-send it to an other Graphql layer. I found it inappropriate to use those inputs because there are typed variable which I need to know before Marshalling them. The difficulties arise because I use a general interface share by several resolver to reconstruct the query, and checking/asserting the type everytimes although I do the same operation for all types which is: "reconstructing the query as a string".

I am currently using graphql.GetRequestContext(ctx).RawQuery as a workaround bur I am not satisfied as I may need to modify the input query in the Business Logic Layer.

dtrckd avatar Apr 18 '20 11:04 dtrckd

@dtrckd I have the same experience with parsing graphql.GetOperationContext(ctx).RawQuery Feel like it is very tedious and error prone. Becomes more complex to deal with when there are multiple arguments/filter inputs in the resolvers and having this custom parser to keep up with schema updates is a nightmare.

@vektah it would be good to access the arguments from the operation context. Any plans to support this feature?

ravisastryk avatar May 05 '20 06:05 ravisastryk

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

stale[bot] avatar Aug 03 '20 06:08 stale[bot]

Nope, stale!

frederikhors avatar Aug 03 '20 08:08 frederikhors

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

stale[bot] avatar Nov 01 '20 15:11 stale[bot]

Nope, stale.

frederikhors avatar Nov 01 '20 16:11 frederikhors

Will this issue get some love? Is there something that needs to be discussed? Something we can do to give it a little push?

mrg0lden avatar Nov 23 '20 18:11 mrg0lden

Any updates on this? Running into the same issue with the newer graphql.GetOperationContext(ctx).Variables

nullism avatar Jan 28 '22 13:01 nullism

Funny, I was lurking around this issue 30 second ago ! Do you guys think it would be a good idea to have access to the input as a map like we have for the variables, accessible for example through the requestContext object, i.e

graphql.Get(Request|Operation)Context(ctx).Inputs

?

dtrckd avatar Jan 28 '22 13:01 dtrckd

I saw the merged PR for #2062. I'm curious has anyone implemented a logger with that yet?

I'm currently using:

// Implement query logger
srv.AroundOperations(func(ctx context.Context, next graphql.OperationHandler) graphql.ResponseHandler {
// Start timer
start := time.Now()

oc := graphql.GetOperationContext(ctx)
var query string
// Make sure this is not an IntrospectionQuery, we don't care about logging those
if !strings.Contains(oc.RawQuery, "IntrospectionQuery") {
	query = oc.RawQuery
	// Use : to make the queries easier to read
	query = strings.Replace(query, " ", ":", -1)
	query = strings.Replace(query, "\t", "", -1)
	query = strings.Replace(query, "\n", "", -1)
}

gc, err := GinContextFromContext(ctx)
if err != nil {
	zap.L().Error("unable to convert gql to gin context for flatquery", zap.Error(err))
}

oh := next(ctx)

jlogger.RequestLogEntry(start, query, gc, requestLogger)

return oh
})

which sort of works but the N+1 problem and spacing is not reliable.

here is RequestLogEntry in case someone else finds this useful with GIN as it was a bit of a pain.

// RequestLogEntry logs a gin HTTP request in JSON format, with some additional custom key/values
func RequestLogEntry(start time.Time, query string, c *gin.Context, requestLog *zap.Logger) {
	// Process Request
	c.Next()

	// Stop timer
	duration := GetDurationInMillseconds(start)
	requestLog.Info("",
		zap.String("client_ip", GetClientIP(c)),
		zap.Float64("duration", duration),
		zap.String("method", c.Request.Method),
		zap.String("path", c.Request.RequestURI),
		zap.Int("status", c.Writer.Status()),
		zap.String("user_id", GetUserID(c)),
		zap.String("referrer", c.Request.Referer()),
		zap.String("user_agent", c.Request.Header.Get("User-Agent")),
		zap.String("gql_query", query),
	)
}

Obsecurus avatar Jun 07 '23 13:06 Obsecurus