keystone
keystone copied to clipboard
Add a new `graphql.typeName` for the `structure` field to support deduplication of GraphQL types
Adds graphql.typeName to structured JSON field to allow structured JSON to have global types
Note if the types don't match it will cause GraphQL runtime errors, this will also not work with relationships
🦋 Changeset detected
Latest commit: 15c0e11f49a00f4b6b025068d0c1d39ea82f35a9
The changes in this PR will be included in the next version bump.
This PR includes changesets to release 1 package
| Name | Type |
|---|---|
| @keystone-6/fields-document | Minor |
Not sure what this means? Click here to learn what changesets are.
Click here if you're a maintainer who wants to add another changeset to this PR
This pull request is automatically built and testable in CodeSandbox.
To see build info of the built libraries, click here or the icon next to each commit SHA.
Latest deployment of this branch, based on commit 15c0e11f49a00f4b6b025068d0c1d39ea82f35a9:
| Sandbox | Source |
|---|---|
| @keystone-6/sandbox | Configuration |
Can you add an example to the test sandbox?
This relates to a more general problem I've hit around GraphQL type re-usage across fields and custom mutations. Currently there's no way (that I know of; please correct me) that let's you share/reuse a GraphQL types between multiple virtual fields, nor is it possible to share a GraphQL type between a virtual fields and GraphQL extensions.
Part if the problem is that, when you need to refer to existing list types for a virtual field, you use the lists argument, eg. graphql.field({ type: lists.Post.types.output, ... }), as documented here. But if you want to do the same thing in extendGraphqlSchema you reference the base object, like this: graphql.field({ type: base.object('Post'), ... }).
In my current project we have managed to reuse types across multiple GraphQL extensions by closuring over base then passing around an object of types, but it kinda sucks. Something like this:
export const extendGraphqlSchema: ExtendGraphqlSchema = graphql.extend((base) => {
const loaders = buildLoaders(base);
const dynamicGqlTypes = buildDynamicGqlTypes(base, loaders);
return {
query: {
dodads: dodadsQuery({ dynamicGqlTypes }),
thingamajigs: thingamajigsQuery({ dynamicGqlTypes }),
search: searchQuery({ base, dynamicGqlTypes }),
},
mutation: {
createDodad: createDodadMutation({ dynamicGqlTypes }),
updateDodad: updateDodadMutation({ dynamicGqlTypes }),
createThingamajig: createThingamajigMutation({ dynamicGqlTypes }),
updateThingamajig: updateThingamajigMutation({ dynamicGqlTypes }),
},
};
});
If you could lazily create and cache them like this...
let dodadGraphQLType;
function getDodadGraphQLType(base) {
if (dodadGraphQLType) return dodadGraphQLType;
dodadGraphQLType = graphql.object({
name: 'DodadOutput',
fields: {
// ...
},
});
return dodadGraphQLType;
}
Then you could just pull the types you needed into each mutation separately but this doesn't work because Keystone creates two schemas (the standard one and another for sudo operations) – you need two distinct GraphQL types (ie. the JS object describing the type) to be created so they can closure the two different base objects and reference the correct list types (which also have standard and sudo versions).
Note, even if this did work, you'd still have problems re-using the types between extensions and virtual fields because (as above) when defining virtual field types you have a lists arg but no base object – you need to abstract that difference as well.
Shouldn't Keystone have a single place we can define GraphQL types for a schema? Then developers can refer to those types by their GraphQL type names (which is always unique) when they're needed for custom queries/mutations, virtual fields, structure fields, etc. This new config (graphql.types?) wouldn't initially replace the existing places you can define GraphQL types, just supplement them.