Skip to content

Interface fields are not generic #720

Closed
@mike-marcacci

Description

@mike-marcacci

Hi there! My apologies if this has been covered elsewhere, but this is something I was very surprised to find missing from GraphQL-js, even after reading the full GraphQL spec. I want to make sure I'm not missing something, and this is in fact impossible.

When an interface is defined with interface-type fields, I assumed that implementations of the parent interface could specify implementations of the child interface in the corresponding field. Any queries that are generic over the parent interface would function as they currently do with inline fragments (since that's the highest level of specificity guaranteed); queries that resolve to an instance of the parent interface which further specifies the field in question should be able to use the child's fields directly.

This would go a long way in codifying conventions that are currently expressed out-of-channel. Let's take the Relay specifications for example. While Node can be defined as an interface, Edge and Connection are just conventions, which feels quite brittle.

Instead, I would have expected this to be possible:

export const Node = new GraphQLInterfaceType({
	name: 'Node',
	description: 'A resource with an ID. Required by the Relay specification.',
	fields: () => ({
		id: { type: new GraphQLNonNull(GraphQLID) }
	}),
	resolveType: () => {
		// ...
	},
});


export const Edge = new GraphQLInterfaceType({
	name: 'Edge',
	description: 'A wrapper around a node that supplies a cursor. This formalizes a requirement of the Relay specification.',
	fields: () => ({
		cursor: { type: GraphQLString },
		node: { type: Node },
	}),
	resolveType: () => {
		// ...
	},
});

export const User = new GraphQLObjectType({
	name: 'User',
	interfaces: () => [Node],
	fields: () => ({
		id: { type: new GraphQLNonNull(GraphQLID) },
		name: { type: GraphQLString }
	}),
});

export const UserEdge = new GraphQLObjectType({
	name: 'UserEdge',
	interfaces: () => [Edge],
	fields: () => ({
		cursor: { type: GraphQLString },
		node: { type: User },
	}),
});

Because User implements Node, then UserEdge should definitely satisfy the requirements of Edge. However, we get the following error:

Error: Edge.node expects type "Node" but UserEdge.node provides type "[User]".

Is this intentionally omitted functionality? Is there a fundamental reason this couldn't work, or is this something worth formally proposing?

Thanks in advance!

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions