-
Notifications
You must be signed in to change notification settings - Fork 2k
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
feat: support for GET method for existing servers #180
Conversation
@DxCx Thanks, I'll review it before the weekend! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is really great, I just had a few comments. I think we should keep the readme as simple as possible, and try to find a good name for the variable that's now called buffer
.
After that, I think it will be sufficient to add just enough tests on the branches to make sure that each branch is covered. Once the PR is ready to be merged, I'll make a corresponding PR to the docs and link it here.
@@ -43,7 +43,23 @@ app.use('/graphql', bodyParser.json(), apolloExpress({ schema: myGraphQLSchema } | |||
app.listen(PORT); | |||
``` | |||
|
|||
### Connect | |||
### Express ( GET ) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do we need separate instructions for GET and POST? I think we could just keep the old ones and explain that you don't need bodyparser with GET (which people can figure out, if they want).
Alternatively, we could comment the lines in the code that enable POST requests and GET requests, and then users can comment out the lines that they don't want/need.
@@ -42,33 +42,48 @@ export function graphqlExpress(options: GraphQLOptions | ExpressGraphQLOptionsFu | |||
} | |||
|
|||
const formatErrorFn = optionsObject.formatError || graphql.formatError; | |||
let buffer; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can we call this requestPayload
or something else that's more descriptive than buffer
?
|
||
switch ( method ) { | ||
case 'get': | ||
return reply(false); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should document the fact that you cannot send batched queries with a GET request.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ack, Where?
i just think that opening batching for GET is going to get monstrous URLs..
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, but I don't think monstrous URLs should be our problem. There's a real risk for that already with normal queries. Most servers don't support URLs longer than 2000 characters, and I think even the introspection query might be longer than that. But that's really not something we should worry about. Maybe we can just put a warning in the README.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a good reason (from the server's perspective) not to allow batched requests via GET?
} | ||
|
||
function getGraphQLParams(payload, isBatch, reply) { | ||
function getGraphQLParams(buffer, isBatch, reply) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why rename this variable?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
to match to the code above it.
i can use requestPayload just like you suggested above
break; | ||
case 'GET': | ||
if ( !req.query ) { | ||
res.statusCode = 500; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't that be a 400?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i just kept the same error code as post.
do you prefer it will be 400?
hi @helfer just seen your comments, i've replied on some and +1 on some. |
break; | ||
case 'GET': | ||
if ( !req.query ) { | ||
res.statusCode = 500; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is not outdated actually.
i kept the same error code as POST.
@helfer, do you prefer it will be 400?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I think 400 is better, because in the case of GET, it means a request was sent without a query, which is definitely not an internal server error. Although... that reminds me that we should also think about how prepared queries would work with GET. I definitely think they should be supported as well, so either this test is in the wrong place, or prepared queries should be a middleware that adds the query to the request before it hits graphql-server.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ohhh
i think you are abit confused (express <-> graphql naming coalition) :)
if you will take a deeper look, req.query for GET is equal to req.body of POST.
which means, in req.query you will get all the query params as key value.
(?key=value&seckey=value) will become:
{
"key": "value",
"seckey": "value",
}
therefore, if i was checking req.query.query, you were correct on your assumption.
is it more clear now? do you still think it should be 400 and not 500?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@helfer ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@DxCx right, but you're checking req.query. If that's not present, it means it wasn't supplied by the user. In that case I would expect a 400, not a 500.
@DxCx looks 99% good to me. Let's add tests and then merge this. The PR has certainly been waiting for long enough! |
@DxCx can you check off all the boxes that can be checked off, and then tell me what else needs to be done before we can merge this? I think GET support would be a nice present for the end of the year 😄 |
i still have work to do here, hoped someone else who needs the feature will take it from there. |
@DxCx okay, sounds good. I think someone else could definitely help or even finish it completely, but for that you should first write down a more detailed list of what still needs to be done. I don't think it would take too long to write that down. 🙂 |
just make sure all new code is covered by spec file, |
hi @helfer we are ready on my side. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks a lot @DxCx! I think if we want to add more tests, we can always do that later. I'd rather not hold this up any longer for no good reason.
One thing we might want to consider adding is a check that makes sure GET requests don't contain mutations. express-graphql does that, and I think it's a neat little feature.
@nnance @HriBB could you take a minute to review the hapi and Koa parts respectively?
|
||
switch ( method ) { | ||
case 'get': | ||
return reply(false); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a good reason (from the server's perspective) not to allow batched requests via GET?
cool. Yeah i think we spoke about it before (either here or on slack) |
i did not understand why not allowing mutation via GET? |
@DxCx The reason express-graphql doesn't allow mutations over GET is that GET requests in HTTP aren't supposed to have any side-effects. Mutations have side-effects by definition, so express-graphql doesn't allow mutations via GET. |
Ok @helfer, sounds right. so you want me to block mutation for all GET requests? |
@DxCx sorry I dropped the ball on this one. Yes, I think blocking mutations on GET would be good, just to match the behavior of express-graphql. Once you're done with that, please ping me on slack and I will take the PR from there and make sure it gets merged soon. Thanks a lot! |
@helfer, no problem, so, we should return 405 error and header says that only POST is allowed.. |
@helfer , please review the commits from today (the rest is just rebase). |
@@ -68,6 +70,13 @@ export async function runHttpQuery(handlerArguments: Array<any>, request: HttpQu | |||
|
|||
let responses: Array<ExecutionResult> = []; | |||
for (let requestParams of requestPayload) { | |||
if ( isGetRequest && !requestParams.query.trim().startsWith('query')) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
According to the spec the query keyword is optional. I know it's annoying, but that's how it is. So we'll have to check here whether it's an anonymous query that starts with {
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, the query isn't necessarily the first thing in the document right? For example:
fragment X on Y { ... }
{ ... }
Is also a valid query.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would check for "document doesn't contain the keyword mutation
outside of all brackets"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@helfer => i was already figured it out, and actually, you were reviewing outdated chunk, which this was already fixed.
@stubailo => about the fragment, this is really usecase i didn't take into consideration,
i'll try to use GraphQL Parser instead to get the exact operation GraphQL will try to execute.
@DxCx Perfect, I think parsing the query is great. And thanks to how the core works it won't be parsed twice. I'll update the docs and roll a new release! |
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
TODO:
Closes #121 , #186