dotansimha / graphql-code-generator

A tool for generating code based on a GraphQL schema and GraphQL operations (query/mutation/subscription), with flexible support for custom plugins.
https://the-guild.dev/graphql/codegen/
MIT License
10.85k stars 1.33k forks source link

Introspection fails to load schema from server #7934

Open Eddy84 opened 2 years ago

Eddy84 commented 2 years ago

Describe the bug

I have installed the latest version of graphql-codegen and used the same config from another project to generate the types. It works on the old project, but fails in the new one.

It seems to me that cross-undici-fetch dependency is the cause of the problems, I tried to override the version but I can't get it to work.

Old project uses:

New Project:

I can exclude the server as the source of problem, because introspection via phpstorm or from the old project works find.

To Reproduce

1.) Create a blank nextjs project 2.) Setup graphql codegen 3.) Create a codegen.yml that loads the schema from remote 4.) Execute codegen

Steps to reproduce the behavior:

  1. My GraphQL schema:

The schema works fine its the fetch that fails.

  1. My GraphQL operations:

Doesn't load the schema from the server.

  1. My codegen.yml config file:
`
schema: 'http://127.0.0.1/service'
documents: ./src/**/*.gql
config:
  strictScalars: true
  scalars:
    Date: string
    DateTime: string
    DateTimeTz: string
    DateTimeUtc: string
    Mixed: any
    Upload: File
    JSON: any
hooks:
  afterAllFileWrite: prettier --write
generates:
  ./src/types/graphql.generated.ts:
    plugins:
      - typescript
    config:
      namingConvention: keep
  src/:
    preset: near-operation-file
    presetConfig:
      extension: .generated.ts
      folder: generated
      baseTypesPath: types/graphql.generated.ts
    plugins:
      - typescript-operations
      - typescript-urql

`

Expected behavior

It should load the schema from the server and generate the types as it did.

Environment:

Additional context

Something went wrong Failed to load schema for "src/"
        Failed to load schema from http://127.0.0.1/service:

        terminated
        TypeError: terminated
    at Fetch.onAborted (/home/eduard/Projects/ecommerce/frontend/node_modules/undici/lib/fetch/index.js:1893:49)
    at Fetch.emit (node:events:390:28)
    at Fetch.terminate (/home/eduard/Projects/ecommerce/frontend/node_modules/undici/lib/fetch/index.js:77:10)
    at Object.onError (/home/eduard/Projects/ecommerce/frontend/node_modules/undici/lib/fetch/index.js:2027:34)
    at Request.onError (/home/eduard/Projects/ecommerce/frontend/node_modules/undici/lib/core/request.js:237:27)
    at errorRequest (/home/eduard/Projects/ecommerce/frontend/node_modules/undici/lib/client.js:1711:13)
    at Socket.onSocketClose (/home/eduard/Projects/ecommerce/frontend/node_modules/undici/lib/client.js:985:5)
    at Socket.emit (node:events:390:28)
    at TCP.<anonymous> (node:net:687:12)

        GraphQL Code Generator supports:
          - ES Modules and CommonJS exports (export as default or named export "schema")
          - Introspection JSON File
          - URL of GraphQL endpoint
          - Multiple files with type definitions (glob expression)
          - String in config file

        Try to use one of above options and run codegen again.
thomasaull commented 2 years ago

I'm facing the same problem and have not found a solution yet :-/

thomasaull commented 2 years ago

I found out something, when I'm not using my local graphql server but instead a public one like https://api.spacex.land/graphql it can fetch the schema. So apparently the problem in this case is on my side. I'm using laravel and laravel-lighthouse.

Edit: Another update: I logged the introspection query graphql-codegen is using on the backend:

query IntrospectionQuery {
  __schema {
    queryType {
      name
    }
    mutationType {
      name
    }
    subscriptionType {
      name
    }
    types {
      ...FullType
    }
    directives {
      name
      description
      locations
      args {
        ...InputValue
      }
    }
  }
}

fragment FullType on __Type {
  kind
  name
  description
  fields(includeDeprecated: true) {
    name
    description
    args {
      ...InputValue
    }
    type {
      ...TypeRef
    }
    isDeprecated
    deprecationReason
  }
  inputFields {
    ...InputValue
  }
  interfaces {
    ...TypeRef
  }
  enumValues(includeDeprecated: true) {
    name
    description
    isDeprecated
    deprecationReason
  }
  possibleTypes {
    ...TypeRef
  }
}

fragment InputValue on __InputValue {
  name
  description
  type {
    ...TypeRef
  }
  defaultValue
}

fragment TypeRef on __Type {
  kind
  name
  ofType {
    kind
    name
    ofType {
      kind
      name
      ofType {
        kind
        name
        ofType {
          kind
          name
          ofType {
            kind
            name
            ofType {
              kind
              name
              ofType {
                kind
                name
              }
            }
          }
        }
      }
    }
  }
}

Running this query with Insomnia is working perfectly fine…

thomasaull commented 2 years ago

I found a workaround by just fetching and saving my schema via node and using this instead. It's just a small script: https://www.apollographql.com/blog/backend/schema-design/three-ways-to-represent-your-graphql-schema/#786d

Note: There is no export for introspectionQuery anymore, instead use:

const { getIntrospectionQuery } = require('graphql')

// In fetch method:
{ query: getIntrospectionQuery() }
ka2n commented 2 years ago

In my environment, the same problem occurs because the server does not return a Content-Length header.

rel: https://github.com/nodejs/undici/issues/1414

denis-gorin commented 2 years ago

I found a workaround by just fetching and saving my schema via node and using this instead.

another way for laravel and local server (http://127.0.0.1:8000/graphql) is to force response header with Content Length

index.php


$response = $kernel->handle(
    $request = Request::capture()
);

$response->header('Content-Length', strlen($response->getContent()))->send();

PS

or create custom middleware and register it for lighthouse (the right way)

LiamKarlMitchell commented 2 years ago

I found that the above Laravel work-around above breaks OPTIONS request from browser, but it did work for generating :) Options request shouldn't have a Content-Length set.

Below is a middleware, which can be added to App/Http/Middlewares and put into lighthouse middlewares config array. It does add it to every POST request however for the graphql endpoint.

https://gist.github.com/LiamKarlMitchell/18f1e43ae6b772864d1b6a6ec1f71ba9

Works for me for a temp work-around until this can be resolved upstream.

MarcusThe commented 2 years ago

@LiamKarlMitchell We noticed that same problem with OPTIONS request and Content-Length.

We solved it likes this in index.php:

$response = $kernel->handle(
    $request = Request::capture()
);

// "Content-Length" header is not allowed in OPTIONS requests.
if (!$request->isMethod('options')) {
    // To be able to run GraphQL codegen.
    $response->header('Content-Length', strlen($response->getContent()));
}

$response->send();
LiamKarlMitchell commented 2 years ago

@MarcusThe , that way more succinct nice! Wonder about the compressed content side of it and if thats actually needed hmm. Also this one doesn't apply to only lighthouse graphql endpoint. I wanted to restrict it to just the area known to have issue.

No fix at the time of writing in the client side https://github.com/nodejs/undici/issues/1414 Maybe it is worth opening an issue with lighthouse to get them to add a work-around there.