graphql-code-generator icon indicating copy to clipboard operation
graphql-code-generator copied to clipboard

duplicate fragments within queries

Open bastiion opened this issue 3 years ago • 2 comments

Describe the bug

When using fragments in queries which use fragements in multiple positions, the fragments are beeing repeated in the string concatenation, so that most server will complain about it Error: There can be only one fragment named "addressFields".

Your Example Website or App

https://github.com/bastiion/graphql-codegen-duplicate-fragments-issue

Steps to Reproduce the Bug or Issue

  1. formulate a query that uses same fragments within two other fragments
  2. run codegen

Expected behavior

fragments must be deduplicated

a work-arround can be ssen here, which would also be an entry point for a bug fix https://github.com/bastiion/graphql-codegen-duplicate-fragments-issue/commit/d08f51ecdd7c3cb6b7af68c7bd24aae5b4a5808a#diff-ca580bb0b9ca66ec74fb97288ee12d571481819ed8e689ea99b2d6ff79dacc24

Screenshots or Videos

No response

Platform

  • "@graphql-codegen/cli": "2.3.0",
  • "@graphql-codegen/fragment-matcher": "~3.3.0",
  • "@graphql-codegen/introspection": "~2.2.0",
  • "@graphql-codegen/typescript": "~2.7.1",
  • "@graphql-codegen/typescript-document-nodes": "~2.3.1",
  • "@graphql-codegen/typescript-operations": "~2.5.1",
  • "@graphql-codegen/typescript-react-query": "~3.6.1",

Codegen Config File

overwrite: true
schema:
  - http://localhost:9002/graphql
documents: 'graphql/**/*.graphql'
generates:
  graphql/generated/index.ts:
    plugins:
      - 'typescript'
      - 'typescript-operations'
      - 'typescript-react-query'
    config:
      pureMagicComment: true
      fetcher:
        func: ../fetcher#useFetchData
        isReactHook: true

Additional context

example query:

fragment addressFields on Address {
  address
  city
}

fragment personFields on Person {
  name
  address {
    ...addressFields
  }
}

query company {
  company {
    name
    address {
      ...addressFields
    }
    employees {
      ...personFields
    }
  }
}

will produce:

export const AddressFieldsFragmentDoc = /*#__PURE__*/ `
    fragment addressFields on Address {
  address
  city
}
    `;
export const PersonFieldsFragmentDoc = /*#__PURE__*/ `
    fragment personFields on Person {
  name
  address {
    ...addressFields
  }
}
    ${AddressFieldsFragmentDoc}`;
export const CompanyDocument = /*#__PURE__*/ `
    query company {
  company {
    name
    address {
      ...addressFields
    }
    employees {
      ...personFields
    }
  }
}
    ${AddressFieldsFragmentDoc}
${PersonFieldsFragmentDoc}`;

bastiion avatar Jul 15 '22 19:07 bastiion

I'm having the same issue, using the URQL codegen

robertherber avatar Aug 03 '22 13:08 robertherber

I'm also having the same issue. @bastiion have you found a solution?

michaelschufi avatar Aug 08 '22 09:08 michaelschufi

I'm also having this issue.

iuuukhueeee avatar Oct 08 '22 15:10 iuuukhueeee

Also having the same issue. Any workarounds would be appreciated!

devunt avatar Oct 11 '22 05:10 devunt

I was able to work around this using a small wrapper around graphql:

import uniqBy from "lodash/uniqBy";
import type { DocumentNode } from "graphql";

/**
 * Removes duplicate fragments.
 * This is a workaround for this issue: @see https://github.com/dotansimha/graphql-code-generator/issues/8103
 * */
export default function fixFragments<Query extends DocumentNode>(
  query: Query
): Query {
  return { ...query, definitions: uniqBy(query.definitions, "name.value") };
}

(You can use other techniques for deduplicating the fragments by name – I used lodash because we are already using it in our project)

Usage:

await graphQLClient.request(
  fixFragments(
    graphql(/* GraphQL */`
      query YourQuery {
        yourType {
          ...InlineFragment1
          ...InlineFragment2
        }
      }
    `)
  )
)

bernharduw avatar Oct 20 '22 16:10 bernharduw

Hi!

Could you try using the dedupeFragments: true option, as follows:

import { CodegenConfig } from "@graphql-codegen/cli";

const config: CodegenConfig = {
  schema: "schema.graphql",
  documents: "document.graphql",
  generates: {
    "types.ts": {
		plugins: [/* your plugin */]
		config: {
			dedupeFragments: true
		}
	},
  },
};

export default config;

charlypoly avatar Oct 24 '22 10:10 charlypoly

For the client preset, the dedupeFragments flag doesn't have an effect. I just upgraded to v1.1.1 of the client preset.

My config:

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

const config: CodegenConfig = {
  overwrite: true,
  schema: "http://localhost:3000/graphql",
  documents: "app/**/*.tsx",
  generates: {
    "app/gql/": {
      preset: "client",
      plugins: [],
      config: {
        skipTypename: true,
        dedupeFragments: true,
      },
    },
  },
};

export default config;

bernharduw avatar Oct 27 '22 16:10 bernharduw

    './generated/gql/': {
      // schema: './generated/schema.graphql',
      schema: 'http://localhost:3000/api/graphql',
      documents: ['lib/apollo/modules/**/*.{graphql,tsx,ts}', '!lib/apollo/modules/cms/**/*.{graphql,tsx,ts}'],
      preset: 'client',
      plugins: [],
      presetConfig: {
        fragmentMasking: false,
        dedupeFragments: true
      }
    },

Adding it to presetConfig also does not work

AssisrMatheus avatar Oct 28 '22 12:10 AssisrMatheus

@tojump, I've released your contribution; thanks!

@AssisrMatheus @bernharduw, could you try with @graphql-codegen/[email protected]? 📦

charlypoly avatar Nov 01 '22 09:11 charlypoly

I can confirm it's working for me with v1.1.2!

Good work @tojump + @charlypoly, thanks! 🎉

bernharduw avatar Nov 01 '22 12:11 bernharduw

Should this be on by default? I would expect this to happen very often when combining fragments to form larger queries.

marco2216 avatar Nov 01 '22 15:11 marco2216

Should this be on by default? I would expect this to happen very often when combining fragments to form larger queries.

@marco2216 I've open an issue to discuss enable options and default with client-preset: https://github.com/dotansimha/graphql-code-generator/issues/8562

charlypoly avatar Nov 02 '22 09:11 charlypoly

@charlypoly i'm getting this issue with client 1.1.3 when i turn on 'typescript' plugin in the array.

  • If i disable the typescript plugin, the duplicates seem to dissapear.
  • also none of typescript config are respected with or without typescript plugin in the plugins array.
import { CodegenConfig } from '@graphql-codegen/cli';

const config: CodegenConfig = {
  schema: 'https://api-sandbox-mumbai.lens.dev/',
  documents: ['src/**/*.tsx'],
  ignoreNoDocuments: true, // for better experience with the watcher
  generates: {
    './src/generated/gql/': {
      preset: 'client',
      plugins: ['typescript-operations', 'typescript-graphql-request', 'typescript-react-query'], //['typescript', 'typescript-operations', 'typescript-graphql-request', 'typescript-react-query'],
      config: {
        enumsAsConst: true,
        // dedupeFragments: true,
        maybeValue: 'T | undefined',
        nonOptionalTypename: true,
        // useTypenameImports: true,
        avoidOptionals: true,
        // fragmentMasking: false,
      },
    },
  },
};

export default config;

ShravanSunder avatar Nov 05 '22 00:11 ShravanSunder

Hi @ShravanSunder,

preset: "client" should not be used with any typescript-* plugin, this is what is leading with duplicate types.

Also, client-preset only support a subset of config options, see [client-preset] allowed options and defaults

charlypoly avatar Nov 07 '22 08:11 charlypoly