-
Notifications
You must be signed in to change notification settings - Fork 1.3k
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
Add TypedDocumentNode string alternative #9137
Merged
Merged
Changes from 27 commits
Commits
Show all changes
35 commits
Select commit
Hold shift + click to select a range
4a7b339
Add unit tests
beerose 504367a
Add TypedDocumentNode string alternative
beerose c7105db
Temporarily use snapshot version of @graphql-typed-document-node/core
beerose 13e48a9
chore(dependencies): updated changesets for modified dependencies
github-actions[bot] 02ed2b3
Update fragment-masking.ts files
beerose 2d5a4e7
Update fragment-masking in dev-test dir
beerose 590b2e2
Update @graphql-typed-document-node/core version in examples
beerose 8e4e93f
Merge branch 'master' into typed-document-string
beerose 7a5d42d
Update test after latest implememntation changes
beerose 3892ba2
Changeset
beerose ef28588
Modify existing example
beerose 961251f
Update typescript-graphql-request example
beerose dbd2053
Merge branch 'master' into typed-document-string
beerose 8005ea3
Add failing test for duplicated fragments
beerose 2c6ee68
Inline fragments in string document mode
beerose 7b073f3
Update example with fragment inlining
beerose db98f71
Support __meta__ with typed document string
beerose 346e1e5
Add TypedDocumentString class to handle metadata
beerose 4e732da
Fix import
beerose 82b75f0
Remove unused import
beerose 56170f6
Merge branch 'master' into typed-document-string
beerose e3120b3
Update @graphql-typed-document-node/core version
beerose 8b5ad73
Merge branch 'master' into typed-document-string
beerose f960ccf
chore(dependencies): updated changesets for modified dependencies
github-actions[bot] ed34330
Update react-query example — use fetch insetad of http executor
beerose 0283daf
Merge branch 'master' into typed-document-string
beerose cfd9380
Add docs
beerose a97276b
Update @graphql-typed-document-node/code version
beerose 2e99784
chore(dependencies): updated changesets for modified dependencies
github-actions[bot] bdc1e7d
Add more info to the dosc
beerose e954bea
Merge branch 'master' into typed-document-string
beerose 0f155b4
Update docs
beerose 5771e6d
Update changeset
beerose 5ce6b4f
Update versions in changeset
beerose a86f4bc
Merge branch 'master' into typed-document-string
beerose File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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/[email protected]` ↗︎](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,7 @@ | ||
--- | ||
'@graphql-codegen/typed-document-node': minor | ||
'@graphql-codegen/gql-tag-operations': minor | ||
'@graphql-codegen/client-preset': minor | ||
--- | ||
|
||
Add TypedDocumentNode string alternative that doesn't require GraphQL AST on the client |
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-alpha-20230314161625-aafb8c1", | ||
"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.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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 this considered a breaking change for the codegen or not? @n1ru4l @saihaj
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 don't think so, people shouldn't use this type with anything else than our generated stuff.
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.
agreed should be fine
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.
Okay, as I think about it a bit more, this change kinda requires people to upgrade their typed document node version. 🤔
In graphql-yoga we treat
peerDependency
upgrades depending on the type of change.e.g.
X.1.1
bump of a peer dependency would be a breaking change1.X.1
bump of a peer dependency would be a minor change1.1.X
bump of a peer dependency would be a patch changeSo I think we should treat this the same here (minor change).
Here the special case is that it is not listed as a peer dependency, but theoretically, it could be one, as people will need to install it, otherwise, their code won't compile with strict package manages like pnpm and yarn that do no hoisting. 🤔
Then, however, whether it should be a peer dependency or not also depends on the location where the code is generated.
So the safest option would be to ship this as a breaking change. I am sure people will open issues if we don't...