doug-martin / nestjs-query

Easy CRUD for GraphQL.
https://doug-martin.github.io/nestjs-query
MIT License
822 stars 142 forks source link

[Question/Feature Request] Support for Apollo Federation #62

Closed zackerydev closed 4 years ago

zackerydev commented 4 years ago

I am attempting to create two APIs using nestjs-query and combine them into a single graph using Apollo Federation.

I am having trouble when combining the two APIs when one API extends a type defined in another schema.

For example: Say I have a user-service that exposes a User Type with the CRUDResolver. This will generate queries, mutations and input types with the prefix, User.

In another service, the reviews-service I extend the User type and add a list of reviews with another CRUDResolver.

When the gateway attempts to combine the two schemas it results in an error messages similar to the following

Field "UserFilter.and" can only be defined once.
Field "UserFilter.or" can only be defined once.
There can be only one type named "UserFilter".
Field "Mutation.deleteOneUser" can only be defined once.
Field "Mutation.deleteManyUsers" can only be defined once

I have got this working locally by adding an option to the CRUDResolver that adds a prefix to the queries, mutations, filter and input types via getDTONames. I was using the service name, this results in queries/mutations in the review service like:

Mutation.deleteOneReviewServiceUser
Query.reviewServiceUser

The gateway then exposes the normal CRUDResolver fields (defined in the user-service) and will resolve queries like:

query {
  user(id: "1") {
   # resolved from user service
    username
    # resolved from reviews service
    averageReviewScore
    reviews {
      content
    }
  }
}

This works decently well for querying a federated schema. However, I'm not sure how it should be handled from nestjs-query's perspective. The prefix could be added before the type (like above) or potentially before the queries all together (e.g. Query.reviewService_user).

Hope this makes sense, I'm happy to contribute this change, just wanted to get some feedback before I go down a particular path.

doug-martin commented 4 years ago

@zgriesinger how do you extend the user type? I haven't seen a code first example of federation in nest. Reading through https://docs.nestjs.com/graphql/federation it looks to be all schema first.

Thats not to say I'm opposed, I havent done research in this area, but I do wonder if there is a better way to extend a type.

zackerydev commented 4 years ago

The code first approach does not have an example in the Nest.js Docs but here is what the DTOs would look like:

// user service
@Directive(`@key(fields: "username")`)
@ObjectType('User')
export class UserDTO {
  @FilterableField()
  username!: string;
}

// review service
@ObjectType('User')
@Directive('@extends')
@Directive('@key(fields: "username")')
export class UserDTO {
  @Directive('@external')
  @Field()
  username: string;

  @FilterableField()
  averageReviewScore: number;
  ...
}

The resolveReference() function in the Resolver is largely the same as the Nest.js docs.

doug-martin commented 4 years ago

Basic federation support added in v0.8.5