Add id
selection to queried fields that implement a specific interface
#9289
Replies: 3 comments 1 reply
-
I don't know if we can automatically add those fields—I don't think so. But something you can do to ensure cache normalization is to use # eslint @graphql-eslint/require-id-when-available: 'error'
# In your schema
type User {
id: ID!
name: String!
}
# Query
query {
user {
name
# error Field `user.id` must be selected when it's available on a type.
}
} |
Beta Was this translation helpful? Give feedback.
-
As @charpeni mentioned, it's probably not correct for codegen to modify the query documents. Instead I've written a custom plugin that outputs an array of typename strings that implement the relevant interface. // @ts-check
const { GraphQLInterfaceType } = require("graphql");
/**
* @type {import("@graphql-codegen/plugin-helpers").CodegenPlugin}
*/
module.exports = {
plugin(schema, _documents, _config) {
const entryInterface = schema.getType("Entry");
if (!entryInterface) {
throw new Error("Missing Entry interface");
}
if (!(entryInterface instanceof GraphQLInterfaceType)) {
console.log(entryInterface);
throw new Error("Entry is not an interface");
}
const entryTypes = schema.getImplementations(entryInterface);
return `export const contentfulEntryTypeNames = ${JSON.stringify(
entryTypes.objects.map((type) => type.name)
)} as const;`;
},
}; This outputs a module like: export const contentfulEntryTypeNames = [
"ComponentAuthor",
"ComponentRichImage",
"ComponentSeo",
"PageBlogPost",
"PageLanding",
] as const; I can then use a urql exchange to modify queries of these types to add the |
Beta Was this translation helpful? Give feedback.
-
I ended up writing my own document transform to look into the schema to know whether there's an import type { Types } from '@graphql-codegen/plugin-helpers';
import { Kind, TypeInfo, buildASTSchema, isObjectType, visit, visitWithTypeInfo } from 'graphql';
/**
* Document transform adding an `id` field to a selection set if the parent type has an `id` field.
*
* Primarily built to ensure cache normalization, but could lead to a lot of `id` fields duplicated in the query.
*/
export const addIdToDocument = {
transform: ({
schema: astSchema,
documents,
}: Pick<
Parameters<Types.DocumentTransformFunction>[0],
'schema' | 'documents'
>): ReturnType<Types.DocumentTransformFunction> => {
const schema = buildASTSchema(astSchema);
const typeInfo = new TypeInfo(schema);
return documents.map((documentFile) => {
if (!documentFile.document) {
return documentFile;
}
documentFile.document = visit(
documentFile.document,
visitWithTypeInfo(typeInfo, {
SelectionSet(node) {
const parentType = typeInfo.getParentType();
if (isObjectType(parentType)) {
const fields = parentType.getFields();
const selectionHasIdField = node.selections.some(
(selection) => selection.kind === Kind.FIELD && selection.name.value === 'id',
);
if (fields['id'] && !selectionHasIdField) {
node.selections = [
...node.selections,
{
kind: Kind.FIELD,
name: { kind: Kind.NAME, value: 'id' },
},
];
}
}
return node;
},
}),
);
return documentFile;
});
},
}; |
Beta Was this translation helpful? Give feedback.
-
Hey! Is it possible to have codegen automatically add a specific field when I query something that implements a specific interface?
i have an
Entry
interface that looks like this:Every time I query a field that implements
Entry
I want the generated document to include thesys { id }
selection so that I can ensure all entries are cached correctly in our normalised cache.Beta Was this translation helpful? Give feedback.
All reactions