-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add TypedDocumentNode string alternative (#9137)
- Loading branch information
Showing
63 changed files
with
1,362 additions
and
821 deletions.
There are no files selected for viewing
5 changes: 5 additions & 0 deletions
5
.changeset/@graphql-codegen_client-preset-9137-dependencies.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
"@graphql-codegen/client-preset": patch | ||
--- | ||
dependencies updates: | ||
- Updated dependency [`@graphql-typed-document-node/core@3.2.0` ↗︎](https://www.npmjs.com/package/@graphql-typed-document-node/core/v/3.2.0) (from `3.1.2`, in `dependencies`) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
--- | ||
'@graphql-codegen/typed-document-node': major | ||
'@graphql-codegen/gql-tag-operations': major | ||
'@graphql-codegen/client-preset': major | ||
'@graphql-codegen/gql-tag-operations-preset': major | ||
--- | ||
|
||
Add `TypedDocumentNode` string alternative that doesn't require GraphQL AST on the client. This change requires `@graphql-typed-document-node/core` in version `3.2.0` or higher. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
# Yoga Persisted Documents Example | ||
|
||
Example for showing how to use GraphQL Code Generator for only allowing the execution of persisted operations. | ||
|
||
[Learn more about Yoga Persisted Operations](https://the-guild.dev/graphql/yoga-server/docs/features/persisted-operations) | ||
|
||
## Usage | ||
|
||
Run `yarn codegen --watch` for starting GraphQL Code Generator in watch mode. | ||
|
||
Run `yarn test` for running a tests located within `yoga.spec.ts`. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
module.exports = { | ||
presets: [ | ||
['@babel/preset-env', { targets: { node: process.versions.node.split('.')[0] } }], | ||
'@babel/preset-typescript', | ||
], | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
// eslint-disable-next-line import/no-extraneous-dependencies | ||
import { type CodegenConfig } from '@graphql-codegen/cli'; | ||
|
||
const config: CodegenConfig = { | ||
schema: './src/yoga.ts', | ||
documents: ['src/**/*.ts'], | ||
generates: { | ||
'./src/gql/': { | ||
preset: 'client-preset', | ||
presetConfig: { | ||
persistedDocuments: true, | ||
}, | ||
config: { | ||
documentMode: 'string', | ||
}, | ||
}, | ||
}, | ||
hooks: { afterAllFileWrite: ['prettier --write'] }, | ||
}; | ||
|
||
export default config; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
module.exports = { | ||
transform: { '^.+\\.ts': 'babel-jest' }, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
{ | ||
"name": "example-persisted-documents-string-mode", | ||
"version": "0.0.0", | ||
"private": true, | ||
"dependencies": { | ||
"graphql-yoga": "3.7.2", | ||
"@graphql-yoga/plugin-persisted-operations": "1.7.2" | ||
}, | ||
"devDependencies": { | ||
"@graphql-typed-document-node/core": "3.2.0", | ||
"jest": "28.1.3", | ||
"babel-jest": "28.1.3", | ||
"@graphql-codegen/cli": "3.2.2", | ||
"@graphql-codegen/client-preset": "2.1.1", | ||
"@babel/core": "7.21.0", | ||
"@babel/preset-env": "7.20.2", | ||
"@babel/preset-typescript": "7.21.0" | ||
}, | ||
"scripts": { | ||
"test": "jest", | ||
"codegen": "graphql-codegen --config codegen.ts", | ||
"build": "tsc", | ||
"test:end2end": "yarn test" | ||
}, | ||
"bob": false | ||
} |
48 changes: 48 additions & 0 deletions
48
examples/persisted-documents-string-mode/src/gql/fragment-masking.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
import { ResultOf, DocumentTypeDecoration } from '@graphql-typed-document-node/core'; | ||
|
||
export type FragmentType<TDocumentType extends DocumentTypeDecoration<any, any>> = | ||
TDocumentType extends DocumentTypeDecoration<infer TType, any> | ||
? TType extends { ' $fragmentName'?: infer TKey } | ||
? TKey extends string | ||
? { ' $fragmentRefs'?: { [key in TKey]: TType } } | ||
: never | ||
: never | ||
: never; | ||
|
||
// return non-nullable if `fragmentType` is non-nullable | ||
export function useFragment<TType>( | ||
_documentNode: DocumentTypeDecoration<TType, any>, | ||
fragmentType: FragmentType<DocumentTypeDecoration<TType, any>> | ||
): TType; | ||
// return nullable if `fragmentType` is nullable | ||
export function useFragment<TType>( | ||
_documentNode: DocumentTypeDecoration<TType, any>, | ||
fragmentType: FragmentType<DocumentTypeDecoration<TType, any>> | null | undefined | ||
): TType | null | undefined; | ||
// return array of non-nullable if `fragmentType` is array of non-nullable | ||
export function useFragment<TType>( | ||
_documentNode: DocumentTypeDecoration<TType, any>, | ||
fragmentType: ReadonlyArray<FragmentType<DocumentTypeDecoration<TType, any>>> | ||
): ReadonlyArray<TType>; | ||
// return array of nullable if `fragmentType` is array of nullable | ||
export function useFragment<TType>( | ||
_documentNode: DocumentTypeDecoration<TType, any>, | ||
fragmentType: ReadonlyArray<FragmentType<DocumentTypeDecoration<TType, any>>> | null | undefined | ||
): ReadonlyArray<TType> | null | undefined; | ||
export function useFragment<TType>( | ||
_documentNode: DocumentTypeDecoration<TType, any>, | ||
fragmentType: | ||
| FragmentType<DocumentTypeDecoration<TType, any>> | ||
| ReadonlyArray<FragmentType<DocumentTypeDecoration<TType, any>>> | ||
| null | ||
| undefined | ||
): TType | ReadonlyArray<TType> | null | undefined { | ||
return fragmentType as any; | ||
} | ||
|
||
export function makeFragmentData<F extends DocumentTypeDecoration<any, any>, FT extends ResultOf<F>>( | ||
data: FT, | ||
_fragment: F | ||
): FragmentType<F> { | ||
return data as FragmentType<F>; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
/* eslint-disable */ | ||
import * as types from './graphql'; | ||
|
||
/** | ||
* Map of all GraphQL operations in the project. | ||
* | ||
* This map has several performance disadvantages: | ||
* 1. It is not tree-shakeable, so it will include all operations in the project. | ||
* 2. It is not minifiable, so the string of a GraphQL query will be multiple times inside the bundle. | ||
* 3. It does not support dead code elimination, so it will add unused operations. | ||
* | ||
* Therefore it is highly recommended to use the babel or swc plugin for production. | ||
*/ | ||
const documents = { | ||
'\n query HelloQuery {\n hello\n }\n': types.HelloQueryDocument, | ||
}; | ||
|
||
/** | ||
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. | ||
*/ | ||
export function graphql( | ||
source: '\n query HelloQuery {\n hello\n }\n' | ||
): typeof import('./graphql').HelloQueryDocument; | ||
|
||
export function graphql(source: string) { | ||
return (documents as any)[source] ?? {}; | ||
} |
57 changes: 57 additions & 0 deletions
57
examples/persisted-documents-string-mode/src/gql/graphql.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
/* eslint-disable */ | ||
import { DocumentTypeDecoration } from '@graphql-typed-document-node/core'; | ||
export type Maybe<T> = T | null; | ||
export type InputMaybe<T> = Maybe<T>; | ||
export type Exact<T extends { [key: string]: unknown }> = { [K in keyof T]: T[K] }; | ||
export type MakeOptional<T, K extends keyof T> = Omit<T, K> & { [SubKey in K]?: Maybe<T[SubKey]> }; | ||
export type MakeMaybe<T, K extends keyof T> = Omit<T, K> & { [SubKey in K]: Maybe<T[SubKey]> }; | ||
/** All built-in and custom scalars, mapped to their actual values */ | ||
export type Scalars = { | ||
ID: string; | ||
String: string; | ||
Boolean: boolean; | ||
Int: number; | ||
Float: number; | ||
}; | ||
|
||
export type Mutation = { | ||
__typename?: 'Mutation'; | ||
echo: Scalars['String']; | ||
}; | ||
|
||
export type MutationEchoArgs = { | ||
message: Scalars['String']; | ||
}; | ||
|
||
export type Query = { | ||
__typename?: 'Query'; | ||
hello: Scalars['String']; | ||
}; | ||
|
||
export type HelloQueryQueryVariables = Exact<{ [key: string]: never }>; | ||
|
||
export type HelloQueryQuery = { __typename?: 'Query'; hello: string }; | ||
|
||
export class TypedDocumentString<TResult, TVariables> | ||
extends String | ||
implements DocumentTypeDecoration<TResult, TVariables> | ||
{ | ||
__apiType?: DocumentTypeDecoration<TResult, TVariables>['__apiType']; | ||
|
||
constructor(private value: string, public __meta__?: { hash: string }) { | ||
super(value); | ||
} | ||
|
||
toString(): string & DocumentTypeDecoration<TResult, TVariables> { | ||
return this.value; | ||
} | ||
} | ||
|
||
export const HelloQueryDocument = new TypedDocumentString( | ||
` | ||
query HelloQuery { | ||
hello | ||
} | ||
`, | ||
{ hash: '86f01e23de1c770cabbc35b2d87f2e5fd7557b6f' } | ||
) as unknown as TypedDocumentString<HelloQueryQuery, HelloQueryQueryVariables>; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
export * from './fragment-masking'; | ||
export * from './gql'; |
3 changes: 3 additions & 0 deletions
3
examples/persisted-documents-string-mode/src/gql/persisted-documents.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
{ | ||
"86f01e23de1c770cabbc35b2d87f2e5fd7557b6f": "query HelloQuery { hello }" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
import { createServer } from 'http'; | ||
import { makeYoga } from './yoga.js'; | ||
|
||
import persistedDocumentsDictionary from './gql/persisted-documents.json'; | ||
|
||
const persistedDocuments = new Map<string, string>(Object.entries(persistedDocumentsDictionary)); | ||
|
||
const yoga = makeYoga({ persistedDocuments }); | ||
const server = createServer(yoga); | ||
|
||
// Start the server and you're done! | ||
server.listen(4000, () => { | ||
// eslint-disable-next-line no-console | ||
console.info('Server is running on http://localhost:4000/graphql'); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
import { graphql } from './gql/index'; | ||
import { makeYoga } from './yoga'; | ||
import persistedDocumentsDictionary from './gql/persisted-documents.json'; | ||
|
||
const persistedDocuments = new Map<string, string>(Object.entries(persistedDocumentsDictionary)); | ||
|
||
const HelloQuery = graphql(/* GraphQL */ ` | ||
query HelloQuery { | ||
hello | ||
} | ||
`); | ||
|
||
describe('Persisted Documents', () => { | ||
it('execute document without persisted operation enabled', async () => { | ||
const yoga = makeYoga({ persistedDocuments: null }); | ||
const result = await yoga.fetch('http://yoga/graphql', { | ||
method: 'POST', | ||
headers: { | ||
'content-type': 'application/json', | ||
accept: 'application/json', | ||
}, | ||
body: JSON.stringify({ | ||
query: HelloQuery, | ||
}), | ||
}); | ||
expect(await result.json()).toMatchInlineSnapshot(` | ||
Object { | ||
"data": Object { | ||
"hello": "Hello world!", | ||
}, | ||
} | ||
`); | ||
}); | ||
|
||
it('can not execute arbitrary operation with persisted operations enabled', async () => { | ||
const yoga = makeYoga({ persistedDocuments }); | ||
|
||
const result = await yoga.fetch('http://yoga/graphql', { | ||
method: 'POST', | ||
headers: { | ||
'content-type': 'application/json', | ||
accept: 'application/json', | ||
}, | ||
body: JSON.stringify({ | ||
query: HelloQuery, | ||
}), | ||
}); | ||
expect(await result.json()).toMatchInlineSnapshot(` | ||
Object { | ||
"errors": Array [ | ||
Object { | ||
"message": "PersistedQueryOnly", | ||
}, | ||
], | ||
} | ||
`); | ||
}); | ||
|
||
it('can execute persisted operation with persisted operations enabled', async () => { | ||
const yoga = makeYoga({ persistedDocuments }); | ||
const result = await yoga.fetch('http://yoga/graphql', { | ||
method: 'POST', | ||
headers: { | ||
'content-type': 'application/json', | ||
accept: 'application/json', | ||
}, | ||
body: JSON.stringify({ | ||
extensions: { | ||
persistedQuery: { | ||
version: 1, | ||
sha256Hash: (HelloQuery as any)['__meta__']['hash'], | ||
}, | ||
}, | ||
}), | ||
}); | ||
|
||
expect(await result.json()).toMatchInlineSnapshot(` | ||
Object { | ||
"data": Object { | ||
"hello": "Hello world!", | ||
}, | ||
} | ||
`); | ||
}); | ||
}); |
Oops, something went wrong.