Graphql: Extend types or merging modules schemas

Created on 9 Sep 2020  路  4Comments  路  Source: nestjs/graphql

I'm submitting a...


[ ] Regression 
[ ] Bug report
[x] Feature request
[ ] Documentation issue or request
[ ] Support request => Please do not submit support request here, instead post your question on Stack Overflow.

Current behavior

Defining the same type in two modules gives me the following error:

Error: Schema must contain uniquely named types but contains multiple types named "User"

A similar issue is discussed here #721, but it's about more than one endpoint, I'm talking about just one.

Expected behavior

The error is very logical because GraphQL doesn't know what to do with by default. However, I'd like to define a type in module A and extend the same type in module B.
This should be possible in a schema-first approach as can be seen here http://spec.graphql.org/June2018/#sec-Object-Extensions, but I'd like it to work in the code-first approach as well.

Currently I can think of the following solutions:

  • I can imagine the schemas from each module are merged/stitched together if a type occurs in two or more modules under the same name. In the forRoot config you can supply a mergeModuleSchema flag to enable the stitching.
  • Add an extend property to the ObjectType decorator like discussed in this issue at type-graphql: https://github.com/MichalLytek/type-graphql/issues/228#issuecomment-467047326

What is the motivation / use case for changing the behavior?

It will enable better separation of concern as I'll be able to split up an API in features instead of having logic all of over the place.

Environment


Nest version: 7.4.4
Nest Graphql version: 7.6.0

For Tooling issues:

  • Node version: 14.7
  • Platform: Mac

Most helpful comment

Thanks for the response @andreialecu

But I'm not sure if that gets me to where I want to be. Because you still create multiple types under different names. I want to extend a type like this, but code-first:
https://www.graphql-tools.com/docs/generate-schema/#extending-types

All 4 comments

I believe you can use mapped types for this functionality: https://docs.nestjs.com/graphql/mapped-types

I'm using something like this without "major" (#1153 #1154) issues:

import { ClubModel } from 'myapp-data-models'; // shared package

@ObjectType('Club')
export class ClubDto extends PickType(ClubModel, [
  'name',
  'city',
  'address',
  'image',
]) {
  @Field()
  public id?: string;

  // other custom fields
}

Thanks for the response @andreialecu

But I'm not sure if that gets me to where I want to be. Because you still create multiple types under different names. I want to extend a type like this, but code-first:
https://www.graphql-tools.com/docs/generate-schema/#extending-types

One other thing you can do is to mark the base class isAbstract: true (@ObjectType({ isAbstract: true })).

It seems that the usage of isAbstract differs between @nestjs/graphql and type-graphql. If I remember correctly, in type-graphql you couldn't use an isAbstract type directly as part of a query or mutation response. You had to inherit it in another type that was not abstract.

However, it seems that Nest's implementation allows you to use an isAbstract type in all circumstances, the only difference seems to be whether it is added to the schema if unused or not. I feel this could be documented more clearly. (I opened #1154 as a result of being confused by this)

Try experimenting with isAbstract, you will likely end up with less duplicated types.

To illustrate, in the example code I posted above, myapp-data-models/ClubModel has @ObjectType({ isAbstract: true }) and I can do this:

@ObjectType()
export class ClubDto extends ClubModel {}

The interesting thing is that you can continue using ClubModel as part of resolvers if you wanted to as well, even if it is abstract, it will work either way.

Being able to extend types from different modules is essential for module isolation and separation of concerns, it fits the single-responsibility principle. It also the direction most GraphQL libraries are taking, although most of them follow a schema-first approach:

  1. graphql-tools with extend keyword and schema merging link
  2. graphql-modules also with extend keyword and schema merging link
  3. @apollo/federation with extend keyword link
  4. nexus with extendType method link
  5. type-graphql aiming to support it with @ObjectType({ extends: true }) for a code-first approach link
Was this page helpful?
0 / 5 - 0 ratings