Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

applyMiddleware corrupts schema with Apollo federation stub types #395

Open
jekabs-karklins opened this issue May 26, 2021 · 6 comments
Open

Comments

@jekabs-karklins
Copy link

jekabs-karklins commented May 26, 2021

We are using Apollo federation https://www.apollographql.com/docs/federation/federation-spec/ where gateway combines two graphql schemas into one unified schema.

We have vanilla ordinary setup for it

We have type user in service A

@ObjectType()
@Directive('@key(fields: "id")')
export class User {
  @Field(() => Int)
  public id: number;

  @Field()
  public firstname: string;

  @Field()
  public lastname: string;
}

And we have type stub type User in service B

@ObjectType()
@Directive('@extends')
@Directive('@key(fields: "id")')
export class User {
  @Directive('@external')
  @Field(() => Int)
  id: number;
}

And we have type Equipment in service B that refers to the user

import { User } from './User';

@ObjectType()
export class Equipment {
  @Field(() => ID)
  id: number;

  // external type
  @Type(() => User)
  @Field({ nullable: true })
  owner?: User;

  @Field(() => Date)
  createdAt: Date;
}

By applying this trivial middleware in the service A

const doNothing: IMiddlewareResolver = async (
  resolve,
  root,
  args,
  context,
  info
) => {
  return resolve(root, args, context, info);
};

GraphQL will be unable to resolve external fields for the User. i.e.
This will work

query {
  equipments {
    id
    name
    owner {
      id
    }
  }
}

But this will start to fail

query {
  equipments {
    id
    name
    owner {
      id
      lastname
    }
  }
}

with error message
Cannot return null for non-nullable field User.lastname.

I was unable to pinpoint what is going on, but I see that the schemas ar slightly different after running applyMiddleware(doNothing)

@durgesh-mfs
Copy link

@jekabs-karklins have you found solution?

I have similar schema design to yours and getting similar error. I was using v4.0.2 everything was working expected but recently I have update to latest version(v6.0.10) and then started getting this issue. I have also tested v4.0.3 seems like working everything as expected but not working in 5.0.0 and above

@caioaletrocadock
Copy link

I have also tested v4.0.3 seems like working everything as expected but not working in 5.0.0 and above

Well, I tested and yes, the last working version is v4.0.3. I would like to know if there will be a solution, because working with a 2 years outdated version isn't ideal.

Also huge thanks, this lib is awesome.

@hos200sa
Copy link

hos200sa commented Oct 3, 2021

hi guys have anyone found a solution?
it is been bugging me with graphql-sheild, resolveRefrence never get called in User service if applyMiddleware is used,
but if removed everything working again.

@martin-trajanovski
Copy link

I tried to applyMiddleware before adding the reference resolvers to the schema and I think that worked without any issues. So it could be a potential/workaround solution to our specific problem. So here is some example code:

 let federatedSchema = buildSubgraphSchema({
    typeDefs: gql(printSubgraphSchema(schema)),
    resolvers: createResolversMap(
      schema
    ) as GraphQLResolverMap<ResolverContext>,
  });

 federatedSchema = applyMiddleware(federatedSchema, logger);

 addResolversToSchema(federatedSchema, referenceResolvers);

if you try to applyMiddleware after adding the resolvers it ends up messing the federation schema and it doesn't resolve the referenceResolvers correctly.

@jekabs-karklins
Copy link
Author

@jekabs-karklins have you found solution?

I have similar schema design to yours and getting similar error. I was using v4.0.2 everything was working expected but recently I have update to latest version(v6.0.10) and then started getting this issue. I have also tested v4.0.3 seems like working everything as expected but not working in 5.0.0 and above

You can try out what @martin-trajanovski is suggesting

@plfx
Copy link

plfx commented Feb 8, 2022

I found a solution. You need to check whether the resolver is one of the type fields for the Apollo Federation spec, and simply make your middleware a no-op when it matches.

(TypeScript)

function isFederationIntrospectionQuery({
  prev,
  key,
  typename,
}: GraphQLResolveInfo['path']): boolean {
  if (prev) {
    return isFederationIntrospectionQuery(prev);
  } else {
    return (key == '_entities' || key == '_service') && typename == 'Query';
  }
}

// ...

applyMiddleware(
      schema,
      async (resolve, root, args, context, info) => {
        if (isFederationIntrospectionQuery(info.path)) {
          // no-op
          return resolve(root, args, context, info);
        }
        
        // etc...

Could this logic get rolled into graphql-middleware, or some utility or option be added to make this easier?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants