Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature Request: graphql Server Chaining #4

Closed
JProgrammer opened this issue May 11, 2016 · 27 comments
Closed

Feature Request: graphql Server Chaining #4

JProgrammer opened this issue May 11, 2016 · 27 comments

Comments

@JProgrammer
Copy link

With the current focus on small independent microservices in the REST world it would be good to be able to chain multiple apollo servers together.

For instance you may have a single user repository in the company. A apollo server would expose a graphql endpoint to query and mutate these users.

An app server then needs to query this database for user name and address etc. the app server would expose via proxy a subset of queries from the user repository server with the schema being read from their.

The graphql proxy connector could then have extension points to transform the incoming/outcoming queries and results from the chained server.

@helfer
Copy link
Contributor

helfer commented May 11, 2016

@JProgrammer This is a pretty interesting idea, but I wonder whether it could actually be easier to just have the user schema in the app server (exposing only the parts you want) and making the query from there. A graphQL server is essentially a proxy, and I'm not sure yet whether it makes sense to chain them, or whether it's better to just push it all into the gateway (the app server).

Or maybe I misunderstood, and you're asking for a way to compose schemas rather than having one graphql server rely upon another for part of a query?

@JProgrammer
Copy link
Author

@helfer The idea is that in the REST world you would have multiple small servers looking after small parts of the system, microservice, so that they can be independently upgraded, maintained, tested etc.

So if graphql/apollo is replacing the data stack of REST then I would like to be able to make small graphql servers to take responsibility of the service logic/database access then in an application or aggregate server that sits across these servers to serve the individual client queries.

@stubailo
Copy link
Contributor

I'm curious what the benefit would be of having the microservices use GraphQL to communicate rather than REST. In my current mental model, the microservices still speak REST, and there is a single GraphQL server that composes all of the data. But perhaps there are significant benefits to using GraphQL everywhere!

@JProgrammer
Copy link
Author

I guess with graphql-tools just being a express middleware there is not a large overhead for microservices to offer both REST and GraphQL.

With the end goal of apollo-server having meteor like reactivity having a server pass reactivity between servers will reduce load and distribute a single read/write operation across multiple clients and make a simple path for inter application communication.

A lot like how meteor server can connect to another meteor server over DDP rather than having to share state over REST.

@helfer
Copy link
Contributor

helfer commented May 29, 2016

Closing this, since it's almost three weeks old. Feel free to continue discussing it, and reopen if you have a specific use case that cannot currently be supported with the current implementation of apolloServer.

@helfer helfer closed this as completed May 29, 2016
@ejaxon
Copy link

ejaxon commented Jul 4, 2016

I am looking at a use case that may be well served by such a feature.

My interest is in open local government data (cities & counties) and, as an extension, in the ways that local government systems make data accessible internally as well. GraphQL strikes me as potentially a great way to provide such data both as a standard internally and via open data portals. Obviously the ability for clients to specify the shape of the data they want is important here (since the use cases that will employ the data aren't known in advance), but the validation and introspection capabilities of GraphQL are especially valuable.

One of the interesting aspects of open government data is that cross-organizational (e.g., different departments or agencies) and even cross-jurisdictional (e.g., the county in which a city is located) data queries are important. In many cases this will be a one-off task and can be left to the client, but I expect some queries to be common and important enough that organizations should provide an aggregated view. This might be done by a city, combining multiple department sources into a common portal or even pointing, say, to county, state or federal sources of related data. Or it might be done by a 3rd party, say an investigative journalism organization or an advocacy group.

It is certainly possible to create some kind of proxy server that could take care of this and perhaps that's the better way, but the fact that such a proxy would certainly need to be able to combine both GraphQL and REST endpoints makes me think that it would be better integrated directly into the GraphQL server. Also, optimizing such queries may be possible, but that again assumes built-in knowledge of the functioning of GraphQL, which resides best inside the same server.

@JProgrammer - perhaps it's worth re-opening this issue to discuss such a use case?

@igrayson
Copy link

I'm also looking at a problem I believe would be served by this capability.

@stubailo

But perhaps there are significant benefits to using GraphQL everywhere!

A large benefit I see is that I only need to express my schema once and in one format (gql), which is shared by my client, and the "end" servers that know how to service various sub-trees of the client's query. All my proxy server(s) should need to know is how to route subtrees of the query to org-specific services.

@rogchap
Copy link

rogchap commented Dec 16, 2016

@stubailo

But perhaps there are significant benefits to using GraphQL everywhere!

The same benefits you get from an API gateway using GraphQL is true for an internal microservice like query batching or caching for example. Why would you go back to REST, just because it's not externally facing?

@AshleyAitken
Copy link

AshleyAitken commented Feb 8, 2017

I am interested in this as well. We have multiple subsystems each with a current REST Query API. Then there is a Gateway that has the public client REST Query API. The Gateway can do integration across the REST Query APIs.

I think it would be great if the Gateway could be a GraphQL server that aggregated the schemas from each subsystem GraphQL server. Clients using the Gateway GraphQL endpoint could then query across all the subsystems.

I guess what I am asking for is a datastore that can be another GraphQL server. I

@jnak
Copy link

jnak commented Feb 8, 2017

Coursera mentioned a similar approach in their graphql talk. I think this is THE missing piece in a micro-service architecture:

  • It allows to decouple as much as one possibly can individual services
  • it encapsulates all domain logic in each service (authentication, permission, etc...)
  • and it exposes one unified graph (which is major pain point of traditional micro-services architecture).

If anyone is interested to work on it, please hit me up :)

@ejaxon
Copy link

ejaxon commented Feb 9, 2017

This is something I might be interested in working on when we hit the need - at the moment we're building just the one API, but I expect we'll get there.

As a side note, we're building some data infrastructure where the base-level GraphQL API is actually automatically generated from metadata - curious if anyone else is doing anything like this. Just did an article on Medium about our approach.

@helfer
Copy link
Contributor

helfer commented Feb 10, 2017

I'm working on a small demo for this exact thing. I'll let you guys know if I have something to show.

@rogchap
Copy link

rogchap commented Feb 10, 2017

We have a micro-services architecture; we've built a graphQL endpoint for our apps to access (public) and then connect to a number of other services for varying things.

We needed to build a new internal (private) service; we built this as another graphQL endpoint and used apollo-client on the server.

This is working really well for us, I just love working with GraphQL over REST, and thanks to apollo-client there was no need to build a separate "client" application for a REST endpoint(s)

The only thing that I found I was repeating my schema definitions (and all the descriptions) in both systems. I also made my internal graphQL quite "flat" with everything on the RootQuery or RootMutation and only my public facing GraphQL more graph like.

@AshleyAitken
Copy link

The question for me is: Assuming we have a number of microservices with GraphQL APIs, how can we best implement a Gateway GraphQL API that offers queries across all the microservices?

It seems that automatically merging GraphQL schemas isn't too difficult (if that is what you wish to do and excepting namespace issues) graphql/graphql-js#223 and we have a tool to help https://github.com/liamcurry/gql

The missing piece, in my opinion, is how to delegate queries for subgraphs of the gateway API to the microservice GraphQL APIs. Can the appropriate resolvers just pass on their subgraph queries? Do we need a client (or clients) on the gateway?

@smolinari
Copy link

smolinari commented Feb 11, 2017

I'm going out on a limb here, and continuing on Ashley's thinking, but there would seem to be a need here to basically prune (or copy) parts of the graph for each microservice, right? So, to define the microservice, we'd need

  1. An automation to prune/ copy the branches of the graph (probably more like a single branch) of the graph for each microservice.
  2. To change the resolver to hit the new microservice with a proper GQL query, instead of it resolving to grabbing the data or service itself in some other format.

I've also been contemplating a microservices architecture, not using GQL, but rather using gRPC (and automating this too), because gRPC has somewhat better (more official/ under one roof) support for different languages than GQL does and all the benefits GQL offers (but specifically for microservices). Plus, gRPC has type definitions which are language agnostic through Proto3. Well, it seems it does. 😄 I'm learning. This is all blue sky thinking on my part, but it shouldn't be too hard to build a parser, which translates GQL to proto3 (or the other way around!!!) and away we go!

Scott

@rkstar
Copy link

rkstar commented Mar 13, 2017

@rogchap how did you implement apollo-client on the server? i'm interested in this too! i have a web server that i need to get connected to my data api via graphql instead of having to set up rest endpoints for server->server data calls.

my current option is to hit the db directly from my web server to do some things and let the client hit the graphql server for everything else. it seems kind of silly to be hitting the db from 2 different places.

@rogchap
Copy link

rogchap commented Mar 13, 2017

It's very simple, we use graphql on an internal server and use apollo client on the other server. Although this does work, I'd recommend gRPC too. We're planning on refactoring to use gRPC for server-to-server and graphQL only as the public facing API

@espoal
Copy link

espoal commented Apr 15, 2017

One use case I thought about is to have one GQL server to handle auth & permission logic, chained with one handling the real data.

So you authenticate with the public facing server, so that you can pass queries to private servers.

I frown upon using gRPC, thus adding another library in my codebase. Using GQL only would be a benefit.

Why can't GQL serve all our needs?

@smolinari
Copy link

Why can't GQL serve all our needs?

It can, once all the necessary tooling is made available. 😄

The only question is, when will it come? Currently GraphQL doesn't support what gRPC does. When it does, then sure. It can cover all the API bases.

Scott

@jnak
Copy link

jnak commented Apr 16, 2017

Why can't GQL serve all our needs?

@servermeta While that was not one of the design goals of graqhql, I think there is not fundamental reasons it could not be a graph enabled rpc layer.

The only question is, when will it come? Currently GraphQL doesn't support what gRPC does.
When it does, then sure. It can cover all the API bases.
In my opinion the main thing missing is more robust error / exception handling. At the moment errors are mostly useful for debugging purposes and not useful for rpcs. You could achieve this in ad-hoc manner (see https://medium.com/@tarkus/validation-and-user-errors-in-graphql-mutations-39ca79cd00bf) but I'd rather have the graphql spec defined this so clients and servers are all on the same page.

@smolinari What do you think is missing in Graphql compared tp gRPC?

J

@smolinari
Copy link

@jnak - These points are off the top of my head and from my limited knowledge.

  1. gRPC supports a number of languages out of the box.
  2. The client code can be automatically generated in any of the supported languages.
  3. gRPC supports full duplex streaming.

Scott

@jnak
Copy link

jnak commented Apr 17, 2017

@smolinari Interesting. With regard to 1. and 2., I think graphql took the route of building very idiomatic client and servers in each language by building a spec. While it is harder to bootstrap as an ecosystem, I think medium term it was right choice. When I look at https://github.com/chentsulin/awesome-graphql, I feel we are almost there. Regarding 3., I'm hopeful that graphql subscriptions (http://graphql.org/blog/subscriptions-in-graphql-and-relay/) can evolve into a solid bi-directional layer. Future looks bright :)

@smolinari
Copy link

@jnak - That's why I said "It can, once all the necessary tooling is made available." 😄

I love GraphQL's future too. Because, I believe it is built much more to be a client based API (if you can call it that). By that I mean, the client has much more flexibility in getting only the data it needs and defining the calls on its own. GraphQL is a great end-user(GUI client)/ server API system. This kind of API system isn't possible with gRPC or rather, gRPC is what I consider more server or service based and works better in a microservices environment. They say you can use gRPC for mobile clients, but I still think GraphQL beats it out in this area of API needs.

Scott

@gilbert
Copy link

gilbert commented Aug 23, 2017

Here's a use case I think solving this issue would fulfill. I have a central graphql server that my client interacts with. Behind it are several legacy REST services. So far, so good.

Next, we want to expose our legacy postgres database to our central server using something like postgraphql or join-monster. This is good because it can give a client access to arbitrary but safe queries. But here's the problem: we want to have our central server handle auth., and then pass the relevant graphql query along to the db afterwards.

It would be nice if there were a way to grab that db subquery, I think.

@stubailo
Copy link
Contributor

BTW we're calling this "Schema Stitching" and here's the implementation! ardatan/graphql-tools#382

hooray!

please chime in there.

@jnak
Copy link

jnak commented Aug 25, 2017

@stubailo That's awesome!! Can't wait to see what people do with this!

@loganpowell
Copy link

... and history is made

evans pushed a commit that referenced this issue Jun 27, 2018
…rver-env (#1259)

* Export polyfills and types separately

* More imports from apollo-server-env

* Initial commit

* Add .npmignore to avoid ignoring lib when publishing

* 0.0.2

* Reorganize code and clean up GraphQLExtension interface

* 0.0.3

* Add support for timing callbacks and add GraphQLExtensionStack

* 0.0.4

* Downgrade target in tsconfig.json from es2015 to es5

* 0.0.5

* Bump `graphql` peerDependency. (#3)

* 0.0.6

* Update dependencies

* 0.0.7

* whenResultIsFinished fix for array results (#4)

* 0.0.8

* [apollo-bot] Update the Issue/PR Templates with auto label (#6)

* Bump `graphql` peerDependency. (#7)

* Update `graphql` peer dependency range to allow 0.13.x. (#8)

* Update `devDependencies` to latest versions. (#9)

* dev: Update TypeScript to latest version, v2.7.2.

* dev: Update `graphql` to latest version, v0.13.2.

* dev: Update jest & dependencies to latest versions.

* dev: Update type definitions for `graphql`, `node` and `jest`.

* Allow `undefined` return values to `GraphQLExtension`'s `format()`. (#10)

In some cases, it's conceivable that the `format()` method may need to abort
its decision to provide extension information at runtime, in the event that
it doesn't have the proper information to return a full-result.

The `format` method already removed false-y results, so this simply changes
the types to allow the same.

* 0.0.9

* Fix lifecycle method invocations on extensions

* 0.0.10

* Add changelog

* Upgrade to TypeScript 2.8

Makes my editor integration happier (a bugfix in tsserver I think)

* Add tslint and prettier

Same configuration as apollo-engine-js

* Remove magic from GraphQLExtensionStack constructor

It's not hard to consistently pass in an actual extension object to this
low-level API.

* New extension API: didStart handlers return didEnd handlers

This is a backwards-incompatible change: GraphQLExtension implementations and
users of GraphQLExtensionStack (ie apollo-server-core) must change their
implementations, if they implement any of the xDidStart/xDidEnd APIs.

This allows "didEnd" handlers to refer to closure variables from the "didStart"
handler rather than needing to store state on the extension.

The new "didEnd" handlers run in the opposite order of the "didStart" handlers,
so that they properly nest.

* 0.1.0-beta.0

* Changelog

* Add magic back into GraphQLExtensionStack constructor

But now it actually gets more context (the execution arguments) and doesn't have
to be a constructor.

* 0.1.0-beta.1

* Export more types

* 0.1.0-beta.2

* Fix lifecycle handlers to pass proper "this"

* 0.1.0-beta.3

* Pass options directly to start handlers; eliminate factory again

* 0.1.0-beta.4

* error handling in didEnd

* 0.1.0-beta.5

* pass multiple errors to EndHandler

* 0.1.0-beta.6

* add willSendResponse

* 0.1.0-beta.7

* prettier

* setFieldResolver for custom fieldResolver

* reverse

* get more initial options into requestDidStart

* 0.1.0-beta.8

* 0.1.0-beta.9

* Actually, we already get the fieldResolver!

* 0.1.0-beta.10

* work without extensionStack

* 0.1.0-beta.11

* 0.1.0-beta.12

* Send errors to willResolveField callback

* 0.1.0-beta.13

* willSendResponse can return a result

* 0.1.0-beta.14

* Revert 1063be8..56912fc

This reverts commit 1063be8..56912fc.

* add PQ options to requestDidStart

* 0.1.0-beta.14

* 0.1.0-beta.15

* Initialize an empty TypeScript/Jest package

Template based on apollo-engine-js

* Basic trace node structure building

* basic timing

* Checkpoint towards signature implementation

The new signature implementation does not try to compress whitespace.

* Basic signature implementation

* progress towards actual reporting

* basic checkpoint for reporting

* 0.0.0-beta.1

* pull in @types/long, since it is in the external api

* 0.0.0-beta.2

* get rid of Long

* 0.0.0-beta.3

* debug log request what happened

* 0.0.0-beta.4

* 0.0.0-beta.5

* correct url

* 0.0.0-beta.6

* request headers

* 0.0.0-beta.7

* leave out a few headers

* 0.0.0-beta.8

* prettier

* move stuff into multiple files, and stop exporting the extension

* lots of doc comments

* address agent.ts XXX comments

* implement privateVariables

simplify API by removing flush() and allowing flush-ers to just call sendReport
directly

* privateHeaders and error tracking

* gzip, signals

* fix test

* 0.0.0-beta.9

* Error handling for reports

* 0.0.0-beta.10

* no need to include boring stacktrace

* 0.0.0-beta.11

* tweak error reporting

* 0.0.0-beta.12

* package-lock update (npm@6?)

* Reduce target report size to 512KB from 4MB.

Load testing revealed that protobuf encoding for large FullTraceReports could
tie up CPU and reduce p99 request latency (eg, to 200ms from 10ms). Reducing the
default target report size spreads out the encoding time and mitigates the
impact on latency.  If this is not acceptable for all users, we may have to
investigate reintroducing agent-side stats aggregation to keep report sizes
small.

* 0.0.0-beta.13

* Encode Traces as they come in

This improves p99 times with little effect on p50 times. It also lets us get rid
of the heuristic average trace size estimation.

* 0.0.0-beta.14

* support PQ fields

* npm audit fix

* 0.0.0-beta.15

* ignore coverage

* Make the default signature more aggressive

We'd rather tell people confused by literal removal to tweak the signature than
tell people causing outages to do so.

* 0.0.0-beta.16

* Remove obsolete files from graphql-extensions and apollo-engine-reporting

* Fix dependencies and configs

* Fix apollo-server-cloudflare to import from apollo-server-env

* Fix compilation and test configs

* Get all tests passing again

* Switch to Lerna independent versioning

* Polyfill promisify for Node < 8 and load polyfills in tests

* ES2016 exponentiation operator is only supported in Node > 6

* add dependency cache for circle

* add missing env dependencies in REST datasource
evans pushed a commit that referenced this issue Jul 3, 2018
evans pushed a commit that referenced this issue Jul 3, 2018
* Initial commit

* 0.0.3

* Replace endOffset with duration in resolver calls

* 0.0.4

* Fix duration

* 0.0.5

* Remove unnecessary schema level resolve function and return schema

* Update README

* 0.0.6

* Update README

* Update dependencies

* 0.0.7

* Update README

* set package.json to point to this repository (#3)

* Update dependencies

Fixes #4.

* 0.0.8

* Add asynciterable support to tsconfig.json

* Skip trace collection when context or context._traceCollector is undefined

Fixes #5.

* 0.0.9

* Rewrite to use graphql-extensions

* 0.0.10

* 0.0.11

* 0.1.0

* Update graphql-extensions dependency and downgrade TS target

* 0.1.1

* Update README

* Update README

* Increase version range for `graphql` peerDependency. (#7)

* 0.1.2

* Update dependencies

* 0.1.3

* [apollo-bot] Update the Issue/PR Templates with auto label (#9)

* Update `graphql` peer dependency range to allow 0.13.x.

* dev: Update TypeScript to latest version, v2.7.2.

* dev: Update jest & dependencies to latest versions.

* dev: Update type definitions for `graphql`, `node` and `jest`.

* Allow `undefined` to return from `format`. (#12)

* Allow `undefined` to return from `format`.

TypeScript 2.7 introduced new "Strict Class Instantiation" rules which,
as the name suggests, require properties which are intended to be set
(eventually, to a type) be set during construction.

Before this change, the `TracingExtension` class was deferred setting these
private properties (namely, `startWallTime`, `endWallTime`, `startHrTime`
and `duration`), but not during instantiation which required setting
marking them as optional and guarding their usage in other methods which
might use those (temporarily `undefined`, if even for a tick) properties.
For example, the expectation that `format` is _only_ called after
`requestDidStart` is not guaranteed with this configuration, even if it is
expected under normal operation.

Therefore, this change adds the additional guarding and updates the `format`
method to return `undefined` in the event that it doesn't have the appropriate
data.

* Update `graphql-extensions` dependency to `~0.0.9`.

Specifically, to take advantage of a type which landed in
`graphql-extensions@0.0.9` thanks to
https://github.com/apollographql/graphql-extensions/pull/10.

* 0.1.4

* Update for graphql-extensions@0.1.0 API (#13)

* Upgrade to TypeScript 2.8

* Add tslint and prettier

* Update for graphql-extensions@0.1.0 API

* 0.2.0-beta.0

* Make work with newest API usage

format() now gets called before the requestDidStart() EndHandler.

* 0.2.0-beta.1

* remove unused files from tracing package

* upgrade packages, fix compilation bugs, and add test
abernix added a commit that referenced this issue Jan 25, 2019
trevor-scheer pushed a commit that referenced this issue May 6, 2020
Drop support for Node.js 6 and update TS compilation targets accordingly.
trevor-scheer pushed a commit that referenced this issue May 6, 2020
…priately.

The `engine` property is just a suggestion, but the `es2017` compilation
target should be appropriate for Node.js 8 and higher.

This is now appropriate since we've dropped Node.js 6 support in the
repository, as was done in #4.
trevor-scheer pushed a commit that referenced this issue May 6, 2020
This partially reverts the changes in f74e10eea31a9093168811b8264cdf12bbd72421 which
changed from using the native `Object.entries` and `Object.values` methods
provided in ES2017 to use `objectEntries` and `objectValues` helpers which
mock that functionality for ES2016.

As we've now dropped support for Node.js and bumped the compilation target
in #4, those abstractions shouldn't be necessary anymore.

This preserves the rest of the changes in the aforementioned commit, such as
the file moves from `federation` to `composition`.
trevor-scheer pushed a commit that referenced this issue May 12, 2020
Drop support for Node.js 6 and update TS compilation targets accordingly.
trevor-scheer pushed a commit that referenced this issue May 14, 2020
Drop support for Node.js 6 and update TS compilation targets accordingly.
trevor-scheer pushed a commit that referenced this issue May 14, 2020
…priately.

The `engine` property is just a suggestion, but the `es2017` compilation
target should be appropriate for Node.js 8 and higher.

This is now appropriate since we've dropped Node.js 6 support in the
repository, as was done in #4.
trevor-scheer pushed a commit that referenced this issue May 14, 2020
This partially reverts the changes in f74e10eea31a9093168811b8264cdf12bbd72421 which
changed from using the native `Object.entries` and `Object.values` methods
provided in ES2017 to use `objectEntries` and `objectValues` helpers which
mock that functionality for ES2016.

As we've now dropped support for Node.js and bumped the compilation target
in #4, those abstractions shouldn't be necessary anymore.

This preserves the rest of the changes in the aforementioned commit, such as
the file moves from `federation` to `composition`.
@github-actions github-actions bot locked as resolved and limited conversation to collaborators Apr 21, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests