Coder Social home page Coder Social logo

gbox-proxy / gbox Goto Github PK

View Code? Open in Web Editor NEW
45.0 2.0 5.0 276 KB

Fast :zap: reverse proxy in front of any GraphQL server for caching, securing and monitoring.

Home Page: https://gbox-proxy.github.io/

License: Apache License 2.0

Go 98.72% Dockerfile 0.06% Smarty 1.22%
cache caching-proxy golang graphql-server proxy-server

gbox's Introduction

Fast โšก reverse proxy in front of any GraphQL server for caching, securing and monitoring.

Unit Tests codecov Go Report Card Go Reference Artifact Hub

Features

  • ๐Ÿ’พ Caching
    • RFC7234 compliant HTTP Cache.
    • Cache query operations results through types.
    • Auto invalidate cache through mutation operations.
    • Swr query results in background.
    • Cache query results to specific headers, cookies (varies).
  • ๐Ÿ” Securing
    • Disable introspection.
    • Limit operations depth, nodes and complexity.
  • ๐Ÿ“ˆ Monitoring (Prometheus metrics)
    • Operations in flight.
    • Operations count.
    • Operations request durations.
    • Operations caching statuses.

How it works

Every single request sent by your clients will serve by GBox. The GBox reverse proxy will cache, validate, and collect metrics before pass through requests to your GraphQL server.

Diagram

Documentation

The GBox documentation can be browsed on Github page.

Inspiration

The GBox has been inspired by many others related work including:

Thanks to all the great people who created these projects!

gbox's People

Contributors

phu96 avatar vuongxuongminh avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

gbox's Issues

Hasura support

FEATURE: Support Hasura GraphQL queries.

Hasura's GraphQL engine exposes a postgres database as a GraphQL API. It's GraphQL syntax differs a bit from what GBox supports. The community edition of Hasura does not supply query caching and the Cloud and Enterprise editions only supply simplistic caching (TTL invalidation only).

I think the only thing that's not compatible out of the box is auto-invalidation.

query MyQuery {
  account(where: {id: {_eq: "1"}}) {
    id email role
  }
}

mutation MyMutation {
  insert_account(
      objects: {
        email: "[email protected]",
        role: "admin"}) {
    returning {
      id
    }
  }

  update_account(
      where: {id: {_eq: "1"}},
      _set: {email: "[email protected]"})

  delete_account(
    where: {id: {_eq: "1"}})
}

Batch queries

now is not possible to do batch queries. I suggest just small changes to give that possibility.
that functionality is actively used by Apollo for optimizing the query execution time.
the suggested changes are attached

        // router.go line 95
        gqlRequests, err := h.unmarshalHTTPRequest(r)
	if err != nil {
		h.logger.Debug("can not unmarshal graphql request from http request", zap.Error(err))
		reporter.error = writeResponseErrors(err, w)

		return
	}
	n := r.Context().Value(nextHandlerCtxKey).(caddyhttp.Handler)

	for _, gqlRequest := range *gqlRequests {
		if err = h.validateGraphqlRequest(&gqlRequest); err != nil {
			reporter.error = writeResponseErrors(err, w)

			return
		}

		h.addMetricsBeginRequest(&gqlRequest)
		defer func(startedAt time.Time) {
			h.addMetricsEndRequest(&gqlRequest, time.Since(startedAt))
		}(time.Now())

		if h.Caching != nil {
			cachingRequest := newCachingRequest(r, h.schemaDocument, h.schema, &gqlRequest)
			reverse := caddyhttp.HandlerFunc(func(w http.ResponseWriter, r *http.Request) error {
				return h.ReverseProxy.ServeHTTP(w, r, n)
			})

			if err = h.Caching.HandleRequest(w, cachingRequest, reverse); err != nil {
				reporter.error = writeResponseErrors(err, w)

				return
			}

			return
		}
	}

	reporter.error = h.ReverseProxy.ServeHTTP(w, r, n)
}

func (h *Handler) unmarshalHTTPRequest(r *http.Request) (*[]graphql.Request, error) {
	gqlRequests := make([]graphql.Request, 0)
	rawBody, _ := ioutil.ReadAll(r.Body)
	r.Body = ioutil.NopCloser(bytes.NewBuffer(rawBody))
	copyHTTPRequest, err := http.NewRequest(r.Method, r.URL.String(), ioutil.NopCloser(bytes.NewBuffer(rawBody))) // nolint:noctx
	if err != nil {
		return nil, err
	}

	requestBytes, err := ioutil.ReadAll(copyHTTPRequest.Body)
	if err != nil {
		return nil, err
	}

	if len(requestBytes) == 0 {
		return nil, graphql.ErrEmptyRequest
	}

	if err = json.Unmarshal(requestBytes, &gqlRequests); err != nil {
		var gqlRequest graphql.Request
		if e := json.Unmarshal(requestBytes, &gqlRequest); e != nil {
			return nil, e
		}
		gqlRequests = append(gqlRequests, gqlRequest)
	}
	for index, request := range gqlRequests {
		if err = normalizeGraphqlRequest(h.schema, &request); err != nil {
			return nil, err
		}
		gqlRequests[index] = request
	}

	return &gqlRequests, nil
}

Getting a CORS preflight check error from my web application

First off, just wanted to say awesome work! I got this hitting my Hasura instance in Docker and the caching seems to works really well, even for nested types. I want to at least get this working and maybe slow roll into production if I can master what needs to be cached.

With that out of the way, I'm using docker-compose to run this with Hasura locally, as an API to my React app. I'm able to hit the playground just fine, but when I try to use it in my web app I get a CORS preflight error due to it missing the access-control-allow-origin header for my app.

I have my compose file configured like so, with localhost:3000 being the app:

image

I also examined the autosave.json file in the container and it's definitely picking up on my allowed origins:

image

Any help would be appreciated, thanks!

Problems with query parsing

We try to use gbox as a reverse proxy in between a headless pimcore graphql instance and a React frontend. We get errors from gbox for queries that work perfectly fine on the normal backend. It seems like the query parser within gbox is the one that throws this error even before the query is sent to the backend. The error reported is:

internal: json: error calling MarshalJSON for type json.RawMessage: invalid character 'c' looking for beginning of value

The Query we are sending is:

query getMixedQuery($offset: Int,  $orderKey: String, $tags: [Int], $tagCondition: String, $filter: String) {
  mixedFeed(offset: $offset, perPage: 1, orderKey: $orderKey, tags: $tags, tagCondition: $tagCondition, filter: $filter) {
    totalCount
    edges {
      __typename
      ...PostFragment
    }
    __typename
  }
}
fragment PostFragment on object_TribelloPost {
  id
  description
  popularity
  postType
  likesCount
  commentsCount
  dateTime
  thumbnail {
    ... on asset {
      fullpath(thumbnail: "content")
      mimetype
      __typename
    }
    __typename
  }
  urlInput {
    path
    __typename
  }

  headline
  images {
    ... on asset {
      fullpath(thumbnail: "content")
      mimetype
      __typename
    }
    __typename
  }
  
  __typename
}

It seems like the parts fullpath(thumbnail: "content") are the ones triggering the error. While it works perfectly fine as long as there is only one thumbnail query, as soon as we add the second one the error comes up.

Any suggestions how we can solve the problem (except stripping out the thumbnail queries ;) )
If this question should not be here as we are not even sure that it is a bug on gbox, feel free to close the issue.

Thanks

subscriptions url

which URL should I use for subscriptions and which headers
I tried with ws://localhost/graphql but get a 404 error,
wss://localhost/graphql raise 404 too
sec-websocket-protocol: graphql-ws
original server give me response

Possible to only cache specific fields?

Hi again,

I've been playing around with this for a little bit now and it's really promising. I want to try to put it in production, but I have a very large app with several fields and I want to take it slow. Specifically, I wanna start by caching only certain fields that I know need caching at this time, instead of my entire graph, and slowly test adding more and more. Is this possible currently?

Bug query result cache key is redundant

Currently, when using multi varies, query result cache key will be redundant on the same query because loop on the map not guaranteed key position for each request.

gbox/caching_plan.go

Lines 245 to 274 in 998a7f2

for name := range plan.VaryNames {
vary, ok := p.caching.Varies[name]
if !ok {
return "", fmt.Errorf("setting of vary %s does not exist in varies list given", vary)
}
for name := range vary.Headers {
buffString := fmt.Sprintf("header:%s=%s;", name, r.Header.Get(name))
if _, err := hash.Write([]byte(buffString)); err != nil {
return "", err
}
}
for name := range vary.Cookies {
var value string
cookie, err := r.Cookie(name)
if err == nil {
value = cookie.Value
}
buffString := fmt.Sprintf("cookie:%s=%s;", cookie, value)
if _, err := hash.Write([]byte(buffString)); err != nil {
return "", err
}
}
}

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.