Amazing extension, thanks a lot!
What's missing for my usecase is a way of adding a formatted GraphQL query as a POST body.
I can do this to send GET with GraphQL queries, but this gets unreadable a lot really quick once the queries get more complicated:
GET http://localhost:2018/graphql/?query={projectList(count:50){id,name}}
Accept: application/json
What i would love to do instead is something like this:
POST http://localhost:2018/graphql
Accept: application/json
Type: GraphQL
query {
projectList(count: 50) {
id
name
}
}
I know that Type is not a header you'd want to specify and adding meta information that is not sent as a header is probably not part of RFC 2616, but it would be a way to format a query accordingly and could even have syntax highlighting for the query included.
Maybe there's others who would be interested in this ?
@codepunkt nice suggestion, I will investigate it first and will update this thread later
@Huachao I'm investigating myself. I can do this:
POST http://localhost:2018/graphql
Accept: application/json
Content-Type: application/json
{
"query": "{
projectList(count: 50) {
id
name
}
}"
}
But when i send it to a simple JSON parser, e.g. body-parser in a nodejs express webserver, it cannot be parsed and i'm unsure why. What happens to linebreaks in JSON strings?
Another idea that i had was having each graphQL query in it's own .gql file, which has syntax highlighting when using the appropriate VSCode extension. This could then be used a little bit like this < ./query.gql.
The GraphQL query itself would only be the string value of the query property in my former example snippet, which is why simply doing < ./query.gql will not work.
Okay. So it seems it would be easier to pass in GraphQL from a file.
# example.http
POST http://localhost:2018/graphql
Accept: application/json
Content-Type: application/json
> ./example.gql
query ProjectLists($defaultCount: Int!) {
projectList(count: $defaultCount) {
id
name
}
}
variables {
"defaultCount": 50
}
If this would work, it would be great!
The actual JSON payload that would be sent in the body is this:
{
"query":"query ProjectLists($defaultCount: Int!) {\n projectList(count: $defaultCount) {\n id\n name\n }\n}",
"variables":{
"defaultCount":50
},
"operationName":"ProjectLists"
}
There is a web frontend to explore and send GraphQL queries called GraphiQL, which basically does this:

They get the variables and query and transform them to corresponding JSON that is sent in a request body. I'm currently looking through their codebase to see if the code doing this is in it's own module - which would be a godsend because we might be able to reuse (parts of) it here.
Detect .gql piped into request body, use module to transform to JSON - done! 馃榿
@codepunkt if use the post request for querying, does the content type should be 'application/graphql'. And I think for a graphql server side normally will support both get and post cases. But the request body is not in JSON.
The POST Request section of the GraphQL docs states this explicitly. Content-Type is application/json.
@codepunkt you are right, however I also found following sentences in the page you provided
In addition to the above, we recommend supporting two additional cases:
If the "query" query string parameter is present (as in the GET example above), it should be parsed and handled in the same way as the HTTP GET case.
If the "application/graphql" Content-Type header is present, treat the HTTP POST body contents as the GraphQL query string.
If you're using express-graphql, you already get these behaviors for free.
So the content-type is "application/graphql" works the same as query string, and no need to transform to JSON
Yes, that is correct. However, this is a way to query a server that almost no one uses and that i've not seen in production ever. Main reason is that it does not support variables, which is an important part of GraphQL.
A lot of GraphQL server libraries don't even implement it and imho there was a discussion somewhere to even remove it from the docs which i couldn't find offhand.
@codepunkt thanks, previously I am not quite familiar with GraphQL. I will consider your suggestions carefully.
@Huachao that you are even considering this, not being familiar with the technology, is amazing. Even if nothing comes from this, thank you!
@codepunkt Thanks for your suggestion. I think it's a must to support GraphQL. And I will work in two aspects:
How can i help? I don't have any experience with vscode exensions, but if you have questions on GraphQL or need a second opinion on anything, let me know!
@codepunkt thanks, I will sure turn to you when I have questions about GraphQL
@Huachao @codepunkt I'm up to help, too. This plugin is amazing and support for graphQl will be real time saver (not only for me, I suppose). I can help with function to serialize the payload - in another words it prepare object that should be sent then (like extract operation name and prepare query value).
Would that help?
@dacz if you can help, that will be fantastic!
Any status on this feature? Love the plugin - GraphQL would be a huge value add for us.
@marija17 sorry for no any update on this feature, since I am busy in my daily work
@Huachao - Thanks for the prompt response!
If I were to explore this myself, are you open to contributions to your plugin? I'm not sure how far you explored this addition - but did you run into any particular challenges or roadblocks you could note?
Alternatively, would you be open to someone using your plugin as a base (with credits given) to create a new plugin particularly for graphql?
@marija17 I really welcome that you can contribute to my extension for the graphql feature. The challenges for me(since I am not familiar with graphql), what kind of request body should we support and how to support. Like following two examples:
POST http://localhost:2018/graphql
Accept: application/json
query {
projectList(count: 50) {
id
name
}
}
and
{
"query": "{
projectList(count: 50) {
id
name
}
}"
}
In both examples, they are not standard json body, that means how should we identify the request body targets for graphql and we should do some additional operations on the request body. BTW, do you think what other features we should support for this feature? Thanks in advance.
thank for the great work so far
just FYI
GraphQL have other requests, not just "query", there is also "mutation" - which cannot be send through GET, only by POST
i've tried the @codepunkt solution for query, which works very nicely, but it doesn't work for mutation :(
````HTTP
POST {{graphqlEndpoint}}
content-type: application/json
{
"mutation": "{ purchaseMovie(input: {id: \"ssss\", pin: \"4444\", deviceType: STB}){ clientMutationId }}"
}
````
@alembiq
i've tried the @codepunkt solution for query, which works very nicely, but it doesn't work for mutation :(
GraphQL mutations over HTTP still use "query" but you'll need mutation in the actual GQL:
POST {{graphqlEndpoint}}
content-type: application/json
{
"query": "mutation { purchaseMovie(input: {id: \"ssss\", pin: \"4444\", deviceType: STB}){ clientMutationId }}"
}
diff --git a/src/utils/httpRequestParser.ts b/src/utils/httpRequestParser.ts
index d9f8520..3c1a960 100644
--- a/src/utils/httpRequestParser.ts
+++ b/src/utils/httpRequestParser.ts
@@ -45,7 +45,10 @@ export class HttpRequestParser implements IRequestParser {
// get headers range
let headers: Headers;
let body: string | Stream;
+ let variables: string | Stream;
let bodyLines: string[] = [];
+ let variableLines: string[] = [];
+ let isGraphQlRequest: boolean = false
let headerStartLine = ArrayUtility.firstIndexOf(lines, value => value.trim() !== '', 1);
if (headerStartLine !== -1) {
if (headerStartLine === 1) {
@@ -55,7 +58,7 @@ export class HttpRequestParser implements IRequestParser {
let headerLines = lines.slice(headerStartLine, headerEndLine);
let index = 0;
let queryString = '';
- for (; index < headerLines.length; ) {
+ for (; index < headerLines.length;) {
let headerLine = (headerLines[index]).trim();
if (['?', '&'].includes(headerLine[0])) {
queryString += headerLine;
@@ -73,6 +76,7 @@ export class HttpRequestParser implements IRequestParser {
// get body range
const bodyStartLine = ArrayUtility.firstIndexOf(lines, value => value.trim() !== '', headerEndLine);
if (bodyStartLine !== -1) {
+ const requestTypeHeader = getHeader(headers, 'x-request-type');
const contentTypeHeader = getHeader(headers, 'content-type') || getHeader(this._restClientSettings.defaultHeaders, 'content-type');
firstEmptyLine = ArrayUtility.firstIndexOf(lines, value => value.trim() === '', bodyStartLine);
const bodyEndLine =
@@ -80,6 +84,16 @@ export class HttpRequestParser implements IRequestParser {
? lines.length
: firstEmptyLine;
bodyLines = lines.slice(bodyStartLine, bodyEndLine);
+
+ if (requestTypeHeader && requestTypeHeader === 'GraphQL') {
+ const variableStartLine = ArrayUtility.firstIndexOf(lines, value => value.trim() !== '', bodyEndLine);
+ if (variableStartLine !== -1) {
+ firstEmptyLine = ArrayUtility.firstIndexOf(lines, value => value.trim() === '', variableStartLine)
+ variableLines = lines.slice(variableStartLine, firstEmptyLine === -1 ? lines.length : firstEmptyLine);
+ isGraphQlRequest = true;
+ }
+ }
+
}
} else {
// parse body, since no headers provided
@@ -99,19 +113,31 @@ export class HttpRequestParser implements IRequestParser {
// parse body
const contentTypeHeader = getHeader(headers, 'content-type') || getHeader(this._restClientSettings.defaultHeaders, 'content-type');
- body = HttpRequestParser.parseRequestBody(bodyLines, requestAbsoluteFilePath, contentTypeHeader);
- if (this._restClientSettings.formParamEncodingStrategy !== FormParamEncodingStrategy.Never && body && typeof body === 'string' && MimeUtility.isFormUrlEncoded(contentTypeHeader)) {
- if (this._restClientSettings.formParamEncodingStrategy === FormParamEncodingStrategy.Always) {
- const stringPairs = body.split('&');
- const encodedStringParis = [];
- for (const stringPair of stringPairs) {
- const [name, ...values] = stringPair.split('=');
- let value = values.join('=');
- encodedStringParis.push(`${encodeURIComponent(name)}=${encodeURIComponent(value)}`);
+ if (isGraphQlRequest) {
+ body = HttpRequestParser.parseRequestBody(bodyLines, requestAbsoluteFilePath, contentTypeHeader);
+ variables = HttpRequestParser.parseRequestBody(variableLines, requestAbsoluteFilePath, contentTypeHeader);
+
+ let graphQlPayload = {
+ query: body,
+ variables: JSON.parse(variables.toString())
+ };
+ body = JSON.stringify(graphQlPayload, null, 2);
+ bodyLines = body.split(EOL)
+ } else {
+ body = HttpRequestParser.parseRequestBody(bodyLines, requestAbsoluteFilePath, contentTypeHeader);
+ if (this._restClientSettings.formParamEncodingStrategy !== FormParamEncodingStrategy.Never && body && typeof body === 'string' && MimeUtility.isFormUrlEncoded(contentTypeHeader)) {
+ if (this._restClientSettings.formParamEncodingStrategy === FormParamEncodingStrategy.Always) {
+ const stringPairs = body.split('&');
+ const encodedStringParis = [];
+ for (const stringPair of stringPairs) {
+ const [name, ...values] = stringPair.split('=');
+ let value = values.join('=');
+ encodedStringParis.push(`${encodeURIComponent(name)}=${encodeURIComponent(value)}`);
+ }
+ body = encodedStringParis.join('&');
+ } else {
+ body = encodeurl(body);
}
- body = encodedStringParis.join('&');
- } else {
- body = encodeurl(body);
}
}
Here is a patch of httpRequestParser.ts that adds support GraphQL
Request looks something like this :
POST http://localhost:60000/graphql HTTP/1.1
content-type: application/json
x-request-type: GraphQL
Authorization: Bearer xxxx
query ($context: ListDataFilesInput) {
listDataFiles(input: $context) {
name,
lastModified
}
}
{
"context": {
"customerCode": "test",
"meshKey": "test",
"dataBucketCode": "test-bucket",
"filter": ""
}
}
####
Pay special attention to x-request-type: GraphQL. This customer-header tells the parser that it's a GraphQL request and it should look for a query/mutation + variables.
@codepunkt @Huachao We have used the update above (from @ferronrsmith) in order to use GraphQL (which we use a great deal). It works very well. Would you be able to apply the patch above?
This plugin is extremely useful and having GraphQL support like this opens up the audience even more.
@Huachao I second that.. GraphQL support like this would be beautiful.. Nice job @ferronrsmith
@ferronrsmith @CorfitzMe @dremekie PR is warmly welcomed, pls feel free to create a PR and I will review it. Thanks in advance
https://github.com/Huachao/vscode-restclient/pull/384
My organization fork uses relaxed-json instead of JSON.parse since it allows developers to better document their JSON before version control.
(Note in cases anyone wants to do this). Example :
POST http://localhost:60000/graphql HTTP/1.1
content-type: application/json
x-request-type: GraphQL
Authorization: Bearer xxxx
query ($context: ListDataFilesInput) {
listDataFiles(input: $context) {
name,
lastModified
}
}
{
context: {
customerCode: 'test', // customer unique identifier
meshKey: 'test', // customer environment (test/prod/demo)
dataBucketCode: 'test-bucket',
filter: '' // mongo filter can be passed here
}
}
####
@codepunkt @marija17 @jon301 @bisyonary @offero @mfulton26 @wprater @CorfitzMe @dacz @alembiq @dremekie with the fantastic patch from @ferronrsmith, the extension has the capability to send the GraphQL request directly. And this feature will be published in next release! You can provide a required query body and optional variables section.
You can easily achieve this by adding a custom http header in your request headers X-Request-Type: GraphQL, or simply type graphql, a built-in snippet will be available for you. Here I use the example provided by @ferronrsmith himself
POST http://localhost:60000/graphql HTTP/1.1
content-type: application/json
x-request-type: GraphQL
Authorization: Bearer xxxx
query ($context: ListDataFilesInput) {
listDataFiles(input: $context) {
name,
lastModified
}
}
{
"context": {
"customerCode": "test",
"meshKey": "test",
"dataBucketCode": "test-bucket",
"filter": ""
}
}
Thanks @ferronrsmith for your contribution 馃槃
@codepunkt @marija17 @jon301 @bisyonary @offero @mfulton26 @wprater @CorfitzMe @dacz @alembiq @dremekie GraphQL support is coming in the latest version 0.22.0, you can download and try it. Thanks for your patience, and @ferronrsmith 's great contribution to this feature.
What about graphql fragment support ?
hi @ferronrsmith, could you please add graphql fragment support also
Most helpful comment
@codepunkt @marija17 @jon301 @bisyonary @offero @mfulton26 @wprater @CorfitzMe @dacz @alembiq @dremekie with the fantastic patch from @ferronrsmith, the extension has the capability to send the GraphQL request directly. And this feature will be published in next release! You can provide a required query body and optional variables section.
You can easily achieve this by adding a custom http header in your request headers
X-Request-Type: GraphQL, or simply type graphql, a built-in snippet will be available for you. Here I use the example provided by @ferronrsmith himselfThanks @ferronrsmith for your contribution 馃槃