diff --git a/README.md b/README.md index 30afa4f1d..a918b18ff 100644 --- a/README.md +++ b/README.md @@ -590,7 +590,7 @@ For Node.js v12 you can use [abort-controller](https://github.com/mysticatea/abo #### Why do I have to install `graphql`? -`graphql-request` uses a TypeScript type from the `graphql` package such that if you are using TypeScript to build your project and you are using `graphql-request` but don't have `graphql` installed TypeScript build will fail. Details [here](https://github.com/prisma-labs/graphql-request/pull/183#discussion_r464453076). If you are a JS user then you do not technically need to install `graphql`. However if you use an IDE that picks up TS types even for JS (like VSCode) then its still in your interest to install `graphql` so that you can benefit from enhanced type safety during development. +`graphql-request` uses methods exposed by the `graphql` package to handle some internal logic. On top of that, for TypeScript users, some types are used from the `graphql` package to provide better typings. #### Do I need to wrap my GraphQL documents inside the `gql` template exported by `graphql-request`? diff --git a/src/index.ts b/src/index.ts index 903a2902b..88c7b9b6a 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,5 +1,7 @@ import crossFetch, * as CrossFetch from 'cross-fetch' -import { OperationDefinitionNode } from 'graphql/language/ast' +import { OperationDefinitionNode, DocumentNode } from 'graphql/language/ast' + +import { parse } from 'graphql/language/parser' import { print } from 'graphql/language/printer' import createRequestBody from './createRequestBody' import { @@ -553,12 +555,10 @@ function getResult(response: Dom.Response): Promise { * helpers */ -function resolveRequestDocument(document: RequestDocument): { query: string; operationName?: string } { - if (typeof document === 'string') return { query: document } - +function extractOperationName(document: DocumentNode): string | undefined { let operationName = undefined - let operationDefinitions = document.definitions.filter( + const operationDefinitions = document.definitions.filter( (definition) => definition.kind === 'OperationDefinition' ) as OperationDefinitionNode[] @@ -566,6 +566,25 @@ function resolveRequestDocument(document: RequestDocument): { query: string; ope operationName = operationDefinitions[0].name?.value } + return operationName +} + +function resolveRequestDocument(document: RequestDocument): { query: string; operationName?: string } { + if (typeof document === 'string') { + let operationName = undefined + + try { + const parsedDocument = parse(document) + operationName = extractOperationName(parsedDocument) + } catch (err) { + // Failed parsing the document, the operationName will be undefined + } + + return { query: document, operationName } + } + + const operationName = extractOperationName(document) + return { query: print(document), operationName } } diff --git a/tests/general.test.ts b/tests/general.test.ts index acc701bcd..ac6ce0ff9 100644 --- a/tests/general.test.ts +++ b/tests/general.test.ts @@ -1,3 +1,4 @@ +import gql from 'graphql-tag' import { GraphQLClient, rawRequest, request } from '../src' import { setupTestServer } from './__helpers' import * as Dom from '../src/types.dom' @@ -179,3 +180,35 @@ test('case-insensitive content-type header for custom fetch', async () => { expect(result).toBe(testData.data) }) + +describe('operationName parsing', () => { + it('should work for gql documents', async () => { + const mock = ctx.res({ body: { data: { foo: 1 } } }) + await request( + ctx.url, + gql` + query myGqlOperation { + users + } + ` + ) + + const requestBody = mock.requests[0].body + expect(requestBody.operationName).toEqual('myGqlOperation') + }) + + it('should work for string documents', async () => { + const mock = ctx.res({ body: { data: { foo: 1 } } }) + await request( + ctx.url, + ` + query myStringOperation { + users + } + ` + ) + + const requestBody = mock.requests[0].body + expect(requestBody.operationName).toEqual('myStringOperation') + }) +})