问题
I have a GraphQL server which is able to serve timeseries data for a specified source (for example, sensor data). An example query to fetch the data for a sensor might be:
query fetchData {
timeseriesData(sourceId: "source1") {
data {
time
value
}
}
}
In my frontend, I want to allow the user to select 1 or more sources and show a chart with a line for each one. It seems like this would be possible by using a query like this:
query fetchData {
series1: timeseriesData(sourceId: "source1") {
data {
time
value
}
}
series2: timeseriesData(sourceId: "source2") {
data {
time
value
}
}
}
Most GraphQL tutorials seem to focus on static queries (e.g. where the only thing that is changing is the variables, but not the actual shape of the request) - but in my case I need the query itself to be dynamic (one timeseriesData request for each of my selected ids).
I have the following constraints:
- Modifying the server's schema is not an option (so I can't pass an array of IDs to the resolver, for example)
- My query is specified using a template string e.g. gql`...`
- I don't want to have to manually build up the query as a string, because that seems like a recipe for disaster and would mean that I lose all tooling benefits (e.g. autocomplete, syntax highlighting, linting)
The stack I'm using is:
- Apollo client (specifically apollo-angular)
- Angular
- TypeScript
- graphql-tag (for defining queries)
Ideally, what I want to do is have some way of merging two queries into one, so that I can define them as per the first example but then join them together in an abstraction layer so that I get a single query like the second example to be sent over the wire.
However I'm not sure how to achieve this because graphql-tag is parsing the query into an AST and I'm struggling to understand whether it's feasable to manipulate the query in this way.
What techniques are there for generating a dynamic query like this, where the shape of the query is not known upfront?
回答1:
I think you could use fragments for this! But you still have to write 2 "queries"
in this case fragments
.
First let's create a fragment
for each timeSeries
, please check your timeSeries query type, I'm going to refer to it as timeseriesDataQuery
const series1Q = gql`
fragment series1 on timeseriesDataQuery {
series1: timeseriesData(sourceId: "source1") {
data {
time
value
}
}
}
}
const series2Q = gql`
fragment series2 on timeseriesDataQuery {
series2: timeseriesData(sourceId: "source2") {
data {
time
value
}
}
}
}
And then just stitch them up in the query:
export const mainQuery = gql`
query fetchData {
...series1
...series2
}
${series1Q}
${series2Q}
`
回答2:
You can use fragment to define common fields, and variable bound @include(if: Boolean)
and @skip(if: Boolean)
directives on that fragment to get dynamic fields that are known at execution time.
来源:https://stackoverflow.com/questions/51322346/graphql-dynamic-query-building