graphql icon indicating copy to clipboard operation
graphql copied to clipboard

Setting header values (Cache)

Open mimol91 opened this issue 3 years ago • 2 comments

I am trying to find a way to set header value (Cache control) How can it be done? I've noticed that even if I wrote own middleware, it's not possible(it does not have any effect) to set header after grapql invoke writeHeader in handler. I am passing my header values in context, but I cant not use it

mimol91 avatar Jun 25 '21 07:06 mimol91

Same issue. Any updates from your side?

Aravind1411 avatar Oct 03 '22 11:10 Aravind1411

@Aravind1411 The current solution that I am using - It allows to set header for specific query Please notice that it has some limitation as it is possible to join multiple queries in GQL.

{
  queryA{ //may have cache 2min
    bar
  }
  queryB {//may have cache 10min
    bar
  }
}

//schema.go
"fooQuery": headers(maxAge(600))(fooQuery)


var noStore = header.NoStore
var maxAge = header.MaxAge

func headers(headers ...http.Header) func(f *graphql.Field) *graphql.Field {
	headerCfg := http.Header{}
	for _, v := range headers {
		for k := range v {
			headerCfg.Set(k, v.Get(k))
		}
	}

	return func(f *graphql.Field) *graphql.Field {
		resolve := f.Resolve
		f.Resolve = func(p graphql.ResolveParams) (interface{}, error) {
			res, err := resolve(p)
			if err == nil {
				header.SetHeadersConfig(p.Context, headerCfg)
			}
			return res, err
		}
		return f
	}
}
//header package
var headersCfgKey = contextKey("headersCfg")

type contextKey string

func HeadersConfigFromCtx(ctx context.Context) http.Header {
	if c, ok := ctx.Value(headersCfgKey).(http.Header); ok {
		return c
	}
	return nil
}

func SetHeadersConfig(ctx context.Context, header http.Header) {
	cfg := HeadersConfigFromCtx(ctx)
	if cfg == nil {
		return
	}
	for k := range header {
		cfg.Set(k, header.Get(k))
	}
}

func Middleware(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		rec := httptest.NewRecorder()
		headers := http.Header{}

		next.ServeHTTP(rec, r.WithContext(context.WithValue(r.Context(), headersCfgKey, headers)))
		result := rec.Result()

		for k := range result.Header {
			w.Header().Set(k, result.Header.Get(k))
		}
		for k := range headers {
			w.Header().Set(k, headers.Get(k))
		}
		w.WriteHeader(result.StatusCode)
		if _, err := w.Write(rec.Body.Bytes()); err != nil {
			w.WriteHeader(http.StatusInternalServerError)
		}
	})
}

func NoStore() http.Header {
	h := http.Header{}
	h.Set("Cache-Control", "no-store, max-age=0")
	return h
}
func MaxAge(duration uint) http.Header {
	h := http.Header{}
	h.Set("Cache-Control", fmt.Sprintf("public, maxage=%d", duration))
	return h
}


and use middleware to set headers

	return server.Start(cancel, header.Middleware(r), host, port)

mimol91 avatar Oct 03 '22 11:10 mimol91