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

What is the response format of a multi-operations query? #29

Closed
twidi opened this issue Jul 6, 2015 · 10 comments
Closed

What is the response format of a multi-operations query? #29

twidi opened this issue Jul 6, 2015 · 10 comments

Comments

@twidi
Copy link

twidi commented Jul 6, 2015

If we have a simple query, with only a SelectionSet:

{
    hero {
        name
    }
}

which is the same as

query HeroNameQuery {
    hero {
        name
    }
}

The tests in graphql.js show this expected answer:

{
    data: {
        hero: {
            name: 'R2-D2'
        }
    }
}

I know we can have many root in one query:

query FetchLukeAndLeiaAliased {
    luke: human(id: "1000") {
        name
    }
    leia: human(id: "1003") {
        name
    }
}

Resulting in this answer:

{
    data: {
        luke: {
            name: 'Luke Skywalker'
        },
        leia: {
            name: 'Leia Organa'
        }
    }
}

But, and it's my main concern, what if we have more that one operation in one query, which seems to be valid in the specifications (examples in http://facebook.github.io/graphql/#sec-All-Variable-Uses-Defined and http://facebook.github.io/graphql/#sec-All-Variables-Used).

For example what is the expected answer for this graphql query (dumb query just for the example):

query LukeAndLeiaNames {
    luke: human(id: "1000") {
        name
    }
    leia: human(id: "1003") {
        name
    }
}

query LukeAndLeiaHomePlanet {
    luke: human(id: "1000") {
        homePlanet
    }
    leia: human(id: "1003") {
        homePlanet
    }
}

I currently have no idea, except that there should be one more level between data and a query result using the name of the operation (which must be unique in the document, as said in the specification).

Or each query have its own "object" response, with data, errors... But in this case how are they "encapsulated"? In a main object with the operation names used as keys ? I could not find anything about this in the specification or the graphql.js tests.

Thanks for your answer, I'm currently writing a python graphql server and it will help me to know that ;)

@twidi twidi changed the title What is the response format of a multi "query" query What is the response format of a multi-operations query Jul 6, 2015
@twidi twidi changed the title What is the response format of a multi-operations query What is the response format of a multi-operations query? Jul 6, 2015
@dschafer
Copy link
Contributor

dschafer commented Jul 6, 2015

For example what is the expected answer for this graphql query (dumb query just for the example):

We'd describe that as a Document at the parser layer that contains two Operations: LukeAndLeiaNames and LukeAndLeiaHomePlanet.

When executing, we need to know which Operation to run; http://facebook.github.io/graphql/#sec-Evaluating-requests covers this in the spec and https://github.com/graphql/graphql-js/blob/master/src/executor/executor.js#L162-168 in the reference implementation.

If you only have one operation in a Document, the common case, then it's implied to run that one. If you have more than one, you specify which operation to run in a given execution.

Hope this helps!

@dschafer dschafer closed this as completed Jul 6, 2015
@twidi
Copy link
Author

twidi commented Jul 6, 2015

@dschafer thanks for your answer but it doesn't help me a lot. I understand how it is supposed to work internally.

But, let's forget the server, consider it a black box and imagine I am a client.

If I send this document:

query LukeAndLeiaNames {
    luke: human(id: "1000") {
        name
    }
    leia: human(id: "1003") {
        name
    }
}

query LukeAndLeiaHomePlanet {
    luke: human(id: "1000") {
        homePlanet
    }
    leia: human(id: "1003") {
        homePlanet
    }
}

What do I get in response from the server?

@dschafer
Copy link
Contributor

dschafer commented Jul 6, 2015

What do I get in response from the server?

An error. The Document contained multiple operations and the client didn't specify which one to run. If you wanted to send that document and run one specific query, you could pass an operationName parameter.

The spec:

To evaluate a request, the executor must have a parsed Document (as defined in the “Query Language” part of this spec) and a selected operation name to run.

The executor should find the Operation in the Document with the given operation name. If no such operation exists, the executor should throw an error. If the operation is found, then the result of evaluating the request should be the result of evaluating the operation according to the “Evaluating operations” section.

In our reference implementation, we implemented a helper where if there's only one operation in the document, it's implied that the operation name is the name of that operation (since that simplifies the common case). But you can't run multiple queries in the same request; if that's the desired behavior, a single query that combines them can be run instead. With fragments, that would become simple:

query LukeAndLeiaNames {
    ...LukeAndLeiaNamesFragment
}

fragment LukeAndLeiaNamesFragment on Query {
    luke: human(id: "1000") {
        name
    }
    leia: human(id: "1003") {
        name
    }
}

query LukeAndLeiaHomePlanet {
    ...LukeAndLeiaHomePlanetFragment
}

fragment LukeAndLeiaHomePlanetFragment on Query {
    luke: human(id: "1000") {
        homePlanet
    }
    leia: human(id: "1003") {
        homePlanet
    }
}

query LukeAndLeiaBoth {
    ...LukeAndLeiaHomePlanetFragment
    ...LukeAndLeiaNamesFragment
}

@twidi
Copy link
Author

twidi commented Jul 6, 2015

Ok I understand, thank you. I understood that the executor could only handle one operation at a time but maybe it could have been called many times one operation after the other.

So now what wonder me is... what's the point of having a document that can contains many operations ?

@twidi
Copy link
Author

twidi commented Jul 6, 2015

And to finish: how the client is supposed to send which operation to run if there are many operations in the document ?

Until now I imagined graphql as a simple GraphQLQuery => Server => Response behavior. The client sending its query in string at a server endpoint. So I'm a bit disappointed by this many-operations document :-/

@dschafer
Copy link
Contributor

dschafer commented Jul 6, 2015

And to finish: how the client is supposed to send which operation to run if there are many operations in the document ?

Up to the server; the reference implementation just has the operationName as another parameter, so I could imagine having a HTTP Param for the operation name.

Until now I imagined graphql as a simple GraphQLQuery => Server => Response behavior. The client sending its query in string at a server endpoint. So I'm a bit disappointed by this many-operations document :-/

This is definitely the right way to think about things; a Document is just an additional concept that can contain many GraphQLQueries. If you send a document with only one query, then graphql is just QueryInDocument => Server => Response. If the document has many queries, then you specify a query so that we can go Document => Query => Server => Response

So now what wonder me is... what's the point of having a document that can contains many operations ?

The use is threefold:

  1. It's often useful to store GraphQL documents in a client side file; we use .graphql files on iOS and Android for this. We wanted the parser to work on these files as well (so the parser is universal), but you can (and we often do) include multiple queries in a single document.
  2. One optimization that can be made (and that we'll discuss in more detail in the future, I'm sure) is "persisting" documents; you send the document to the server who stores it for you and gives you an identifier for that, that way you don't have to send the whole document string up every time. If you do that, you can send a document up with all your queries, and then pass the document ID + operation name over the wire. This would optimize bytes being sent from the client to the server.
  3. It's possible to write a "batch" API for GraphQL, where you use the results from one query as the parameters to another. We're still working out the exact details there, but if you do that, it's useful to specify multiple queries in a single document; you'd specify which one you wanted to run at the start and the relationships between them.

You're absolutely correct that in the model we describe (pass a string to the server, get a response back), a Document with multiple operations isn't that useful; we wanted to permit it for the reasons described above.

@twidi
Copy link
Author

twidi commented Jul 6, 2015

Ok thanks a lot for your answers. I started to think like that about the argument for the operation to run, and for the "storage" of queries on the server (like redis does for lua scripts).

I now have a better understanding of the whole thing and can go back to my graphql server in python.

Thanks a lot.

@dschafer
Copy link
Contributor

dschafer commented Jul 6, 2015

Thanks for the great questions!

@verneleem
Copy link

@dschafer I saw this and it peaked my curiosity. Did this ever make it into the spec? I haven't been able to find it anywhere yet.

It's possible to write a "batch" API for GraphQL, where you use the results from one query as the parameters to another. We're still working out the exact details there, but if you do that, it's useful to specify multiple queries in a single document; you'd specify which one you wanted to run at the start and the relationships between them.

@benjie
Copy link
Member

benjie commented Nov 9, 2020

There's not an official spec for Facebook's implementation as far as I know; but Hot Chocolate have great documentation around their implementation of it via the @export directive: https://chillicream.com/docs/hotchocolate/v10/execution-engine/batching/#introduction there's also quite a long discussion here: graphql/graphql-over-http#5

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants