golang pass fiber context to reqular context for graphql

In my golang project I am using Fiber (https://gofiber.io/) as a router and somewhere in code I am setting some values in fibers context. Now I am adding graphql (https://github.com/99designs/gqlgen) endpoints and in resolvers context I need to access values set in fibers context. In router I added the following:

import (
"github.com/gofiber/adaptor/v2"
"github.com/gofiber/fiber/v2"

gql_handler "github.com/99designs/gqlgen/graphql/handler"
)

server := gql_handler.NewDefaultServer(generated.NewExecutableSchema(
    generated.Config{
        Resolvers:  h.createResolvers(),
        Directives: h.createDirectives(),
        Complexity: h.createComplexity(),
    },
))

route := r.Group("/graph")
route.Get("/playground", adaptor.HTTPHandlerFunc(playground.Handler("Graphql Playground", "/graph")))
route.All("", adaptor.HTTPHandler(server))

so somewhere in my code I do:

func (h *Handler) MyFunc(c *fiber.Ctx) error {
  //setting value in fibers context
  c.Locals("MyTestValue", "some_value") 
  return nil
}

and in my graphql resolver I want to access it:

func (r *queryResolver) MyQuery(ctx context.Context) (bool, error) {
    
    fmt.Println(ctx.Value("MyTestValue"))//returns nil
    return true, nil
 }

I tried to create a middleware to pass fiber context into regular context (as it is made for gin router, for example) but it did not work:

func FiberContextToContext() fiber.Handler {
   return func(c *fiber.Ctx) error {
     ctx := context.WithValue(c.Context(), "FiberContextKey", c)
     //c.Context() = c.WithContext(ctx)//errors here
     return c.Next()
   }
}

Any ideas how to fix it would be welcome. Thank you.

First note: if you want to change the value behind a pointer, you’ld need to apply a construction like:

requestCtx := c.Context()
*requestCtx = myNewConxt

Second note: this won’t work because in you case ctx is not of type fasthttp.RequestCtx (fasthttp.RequestCtx implements context.Context, not the other way around.)

So, in general you’re right, with fiber you set Locals, which basically just sets a fasthttp.UserValue. And there’s where the problems begin. UserValues get “lost” after the conversion with adaptor. There’s an open issue for that, but it’s not an easy one to solve.

Effectively, this is where your precious local variables end up:
context.Context with local_user_context object
So they are hidden in an object behind the key "__local_user_context__".

My workaround is this:


// FindValue enables support for values residing either directly in the given context or in the wrapped fiber UserValues hidden by the fiber Adaptor conversion.
func FindValue(ctx context.Context, key any) (value any) {
    if value = ctx.Value(key); value == nil {
        value = getUserCtxFromFiberRequestCtx(ctx).Value(key)
    }
    return value
}

// getUserCtxFromFiberRequestCtx allows to access util-server specific context values for contexts which come from the fiber Adaptor.
// This method returns either the hidden user context in case it's a proper Adaptor, context or otherwise just the incoming adaptor.
func getUserCtxFromFiberRequestCtx(ctx context.Context) (extracted context.Context) {
    if localUserCtx := ctx.Value("__local_user_context__"); localUserCtx != nil {
        if localUsrCtxCast, ok := localUserCtx.(context.Context); ok {
            return localUsrCtxCast
        }
    }
    return ctx
}

It’s not great and might break when this key is rename – or even worse, becomes a private type instead of a string. Maybe other users can come up with better ideas.

In general, it’s to just rely on fiber/fasthttp handlers. But sadly for gqlgen that’s not possible – my screenshot stemps from exactly the same scenario. Still you should keep the following considerations in mind:

  • Does the middleware you want to apply to modify the “context” has to be fiber middleware? Otherwise, you could just set regular http middleware and move it into the adaptor.
  • Is it worth to go with fiber here at all? With the usage of the adaptor, I expect that most of the gain in performance to be lost, b/c fiber (resp. openfaas) is based on reusing objects, and this gets dropped after the conversion.

Leave a Comment