Skip to content
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 TypedDocumentNode #596

Open
1 task done
jyasskin opened this issue Sep 18, 2024 · 5 comments · May be fixed by #609
Open
1 task done

[FEAT]: Support TypedDocumentNode #596

jyasskin opened this issue Sep 18, 2024 · 5 comments · May be fixed by #609
Labels
Status: Up for grabs Issues that are ready to be worked on by anyone Type: Feature New feature or request

Comments

@jyasskin
Copy link

Describe the need

It would be nice if calling graphql(SomeAppropriateConstant) would apply the appropriate Typescript types to the input variables and return value. The graphql-codegen ecosystem has tooling to compile queries to that appropriate constant, but it needs a small amount of integration with request libraries to propagate the types.

https://github.com/dotansimha/graphql-typed-document-node?tab=readme-ov-file#how-can-i-support-this-in-my-library has instructions. The main cost is in adding a dependency on that type-only library.

SDK Version

No response

API Version

No response

Relevant log output

No response

Code of Conduct

  • I agree to follow this project's Code of Conduct
@jyasskin jyasskin added Status: Triage This is being looked at and prioritized Type: Feature New feature or request labels Sep 18, 2024
Copy link

👋 Hi! Thank you for this contribution! Just to let you know, our GitHub SDK team does a round of issue and PR reviews twice a week, every Monday and Friday! We have a process in place for prioritizing and responding to your input. Because you are a part of this community please feel free to comment, add to, or pick up any issues/PRs that are labeled with Status: Up for grabs. You & others like you are the reason all of this works! So thank you & happy coding! 🚀

@jyasskin
Copy link
Author

Oops, you probably actually need to use the TypedDocumentString type that's not actually exported from graphql-typed-document-node: dotansimha/graphql-typed-document-node#163.

@kfcampbell kfcampbell moved this from 🆕 Triage to 🔥 Backlog in 🧰 Octokit Active Sep 23, 2024
@kfcampbell kfcampbell added Status: Up for grabs Issues that are ready to be worked on by anyone and removed Status: Triage This is being looked at and prioritized labels Sep 23, 2024
@fpapado
Copy link

fpapado commented Nov 3, 2024

I would love to work on this! I just noticed that @graphql-typed-document-node/core exports a DocumentTypeDecoration type as its lowest-common-denominator kind of type, so I believe it is possible to add an overload in octokit, without needing to name TypedDocumentString directly 👀

@fpapado fpapado linked a pull request Nov 3, 2024 that will close this issue
4 tasks
@klippx
Copy link
Contributor

klippx commented Jan 31, 2025

Manually typing a trivial query is one thing, but what about big actual production queries and making changes to them? And the fact that graphql schema changes, stuff becomes nullable or non-nullable, enums get added, etc.

I think I might be able to show you a better way to solve this problem that scales.

What you should do is to use graphql-codegen and this preset: https://the-guild.dev/graphql/codegen/plugins/presets/near-operation-file-preset

Step 1, create a codegen.ts:

import type { CodegenConfig } from '@graphql-codegen/cli'

const githubConfig: CodegenConfig = {
  schema: {
    'https://api.github.com/graphql': {
      headers: {
        Authorization: `token ${process.env.GITHUB_TOKEN}`,
        'User-Agent': 'graphql-federation-graphiql',
      },
    },
  },
  documents: 'src/server/routers/**/*.gql',
  config: {
    enumsAsTypes: true,
    scalars: {
      URI: 'string',
      DateTime: 'string',
    },
  },
  generates: {
    'src/generated/workflow.ts': { plugins: ['typescript'] },
    'src/': {
      preset: 'near-operation-file',
      presetConfig: {
        extension: '.generated.tsx',
        baseTypesPath: 'generated/workflow.ts',
      },
      plugins: ['typescript-operations'],
    },
  },
}

export default githubConfig

Step 2: Define a query. Example: HelmReleasesFileContent.gql:

query HelmReleasesFileContent(
  $owner: String!
  $repoName: String!
  $dirPath: String!
) {
  repository(owner: $owner, name: $repoName) {
    helmReleases: object(expression: $dirPath) {
      ... on Tree {
        __typename
        entries {
          __typename
          name
          object {
            __typename
            ... on Blob {
              text
            }
          }
        }
      }
    }
  }
}

Step 3: Run codegen. You will get an auto generated file HelmReleasesFileContent.generated.tsx:

import * as Types from '../../../../../generated/workflow.js'

export type HelmReleasesFileContentQueryVariables = Types.Exact<{
  owner: Types.Scalars['String']['input']
  repoName: Types.Scalars['String']['input']
  dirPath: Types.Scalars['String']['input']
}>

export type HelmReleasesFileContentQuery = {
  __typename?: 'Query'
  repository?: {
    __typename?: 'Repository'
    helmReleases?:
      | { __typename?: 'Blob' }
      | { __typename?: 'Commit' }
      | { __typename?: 'Tag' }
      | {
          __typename: 'Tree'
          entries?: Array<{
            __typename: 'TreeEntry'
            name: string
            object?:
              | { __typename: 'Blob'; text?: string | null }
              | { __typename: 'Commit' }
              | { __typename: 'Tag' }
              | { __typename: 'Tree' }
              | null
          }> | null
        }
      | null
  } | null
}

Step 4: Use this in your code:

import type { HelmReleasesFileContentQuery } from './HelmReleasesFileContent.generated.jsx'
import HelmReleasesFileContent from './HelmReleasesFileContent.gql'

const { repository }: HelmReleasesFileContentQuery = await graphql({
  query: print(HelmReleasesFileContent),
  owner,
  repoName,
  dirPath,
})

As you can see this gave me 100% type safety and there was no manual work involved, just codegen. Now THAT is good developer experience!

Screenshot 2025-01-31 at 10 57 21 PM

And I might add that this was a very simple query. But still, typing this manually would have been quite an ordeal. Imagine what a nightmare it would be to create an advanced query. And the Github GQL types changes (enums get added, removed, etc). Types really is something that has to be automated 100%.

I think we can close this issue and say that the solution exists and is outside the scope of this package.

@fpapado
Copy link

fpapado commented Feb 1, 2025

Thanks for your reply @klippx, I can tell you put time and energy into researching and writing it 😌

I think we are all in agreement about not manually typing queries, and that the graphql-codegen ecosystem would seamlessly allow typing both the response data, and also the query variables.

Your example works, and is similar to what many folks do in order to get type-safety. One thing that is missing is typing the query variables, in order to keep both parts of the query in sync. The latter can be added by a type assertion at the call-site, or for example by writing a small wrapper around octokit graphql, to tie responses and variables into one interface. While that is what we do at work, and again I suspect many folks do that, the crux of this issue is to lift this interface onto octokit graphql's API. In the graphql-codegen ecosystem, TypedDocumentNode/TypedDocumentString is the main such interface.

I mention this because I interpreted your message as positing the example as an alternative to this issue. But I then think this issue leans more into the codegen automation that we agree is good, by papering over both the variable and result types in one interface, with all the codegen convenience.

I can motivate this with code examples, but I see you already spotted the relevant PR, so I'll wait for a bit. Let me know if this makes sense or if you want more information 😊

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Status: Up for grabs Issues that are ready to be worked on by anyone Type: Feature New feature or request
Projects
Status: 🔥 Backlog
Development

Successfully merging a pull request may close this issue.

4 participants