-
Notifications
You must be signed in to change notification settings - Fork 1.8k
GraphQL query Support #3433
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
Comments
@lvauvillier Hey Luc! Thanks for posting this one! I'm curious if you have any ideas on how to handle long polling over some long periods of time? I see you propose to handle it server-side. What if the load balancer has a timeout for HTTP requests? |
@paveltiunov this is a good question. For my current usage we assume that queries takes a reasonable time (our pre-aggregations covers 100% of possible dashboard queries). We just handle the "continue wait" response using promises and delays. This is the getCubeResults function we use in our resolvers: export async function getCubeResults(
query: CubeQuery,
delay = 500
): Promise<any> {
const token = createToken();
const headers = { Authorization: `Bearer ${token}` };
const url = `${process.env.CUBEJS_API}/cubejs-api/v1/load?${encode({
query: JSON.stringify(query)
})}`;
const response = await fetch(url, { method: "GET", headers });
if (!response.ok) {
throw Error(`Error querying cubejs api: status ${response.status}`);
}
const json = await response.json();
if (json.error === "Continue wait") {
await new Promise((resolve) => setTimeout(resolve, delay));
return getCubeResults(query, delay * 1.2); // increase delay
}
return json;
} I dont think GraphQL is designed to handle long running queries. |
A first implementation is available (see #3555). All feedbacks are welcomed |
New spec is available in PR #3555. |
* feat(gateway): Add GraphQL proxy * Add missing hour granularity * Add graphql as regular dependency * Use apiGateway.load() instead of fetch() to get results * New api design and filter argument * Move granularity from args to fields * Lint * Non null members * Use compilerApi instance to cache graphql schema Fixes #3433
Context
Is your feature request related to a problem? Please describe.
A previous issue was opened on this topic (#1765) but was closed due to query format design and ResultSet issue.
As we already run a working graphql API on top of cubeJS REST API on production, I wanted to share here the design API we choosed and how we solved it.
I hope this will start an official support of graphQL API.
Describe the solution you'd like
There is two possible solutions:
We will here choose the first solution to benefit of existing optimization, security, query rewrite etc...
API design
Context
GraphQL APIs returns data in the same shape as they were requested. This constraint dont allow a perfect mapping to the existing REST api and its response.
Example
Response
Spec
API Generation
Framework
We need to use a
code-first
graphql framework to automatically generate entities using the cube schema definition.Example here of api generation using Data model (using nexus framework):
https://github.com/prisma/nexus-prisma
https://github.com/graphql-nexus/nexus-plugin-prisma
Implementation
Resolving steps
1 - For each at each
<CubeName>
, a query has to be generated. If more than one Cube are requested in the same graphQL query, we can use a DataLoader to blend generated queries at the end and make a unique REST API call.2 - At
<CubeName>
level, we have only the arguments (filters, limit, offset, etc.). We need to walk through the children nodes and collect all measures, dimensions and granularity. To achieve this task we can use theinfo
argument to get the graphQL query AST. Wee need to take care of graphQL directives (@if, @Skip, @include) to build a valid query.3 - Send the query to the existing cubejs REST API, handle the "Continue Wait" response, handle errors and re-shape the result to match the graphQL tree
Custom resolvers
With the current api if we wanted to query by dimension (eg. by product) and if this dimension is stored as an id (productId), we will get only the productId in the result.
What if we wanted to display a nice graph with the product Name? We can add the name in the cube, but what if we need more data? this can lead to issues especially a waste of data if we use preaggregations.
We can use here the GraphQL capabilities to create a new Product entity and use custom resolvers.
the entity name and resolvers can be added to the cube schema definition. Only the productId will be used to resolve the entity.
Example:
The text was updated successfully, but these errors were encountered: