maticzav / graphql-shield

🛡 A GraphQL tool to ease the creation of permission layer.

Home Page:https://graphql-shield.com

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Issues with __resolveReference in Apollo Federated Schema

shayzun opened this issue · comments

I am using Graphql-Shield in an Apollo Federation architecture, and there seems to be an issue when referencing entities from another service. The __resolveReference resolver never gets called after running the permission rule.

index.js

const { ApolloServer } = require('apollo-server-cloud-functions')
const { buildFederatedSchema } = require('@apollo/federation')
const { applyMiddleware } = require('graphql-middleware')
const resolvers = require('./resolvers')
const permissions = require('./permissions')
const typeDefs = require('./schema')

const server = new ApolloServer({
	playground: true,
	introspection: true,
	schema: applyMiddleware(
        buildFederatedSchema([{
	      typeDefs,
	      resolvers
      }]),
    permissions
  )})

Schema

type Account @key(fields: "id") {
		id: ID!,
		username: String!,
		createdAt: String!,
		lastLogin: String,
		passwordLastUpdated: String!,
		emails: [Email],
		phones: [Phone]
	}

	type Email {
		id: ID!,
		emailAddress: String!,
		isPrimary: Boolean!,
		isVerified: Boolean!,
		lastVerificationSent: String!
	}

	type Phone {
		id: ID!,
		phoneNumber: String!,
		isPrimary: Boolean!,
		isVerified: Boolean!,
		lastVerificationSent: String!
	}

Permissions

const permissions = shield({
	Query: {
		// Some queries
	},
	Mutation: {
		// Some mutations
	},
	Account: allow
},
{
	allowExternalErrors: true
})

Error
When I perform a query that selects fields from the Account entity, I get the following error

"errors": [
    {
      "message": "Cannot return null for non-nullable field Account.username.",
      "path": [
        "_entities",
        0,
        "username"
      ],
      "extensions": {
        "code": "INTERNAL_SERVER_ERROR",
        "serviceName": "ACCOUNTS",
        "query": "query ($representations: [_Any!]!) {\n  _entities(representations: $representations) {\n    ... on Account {\n      username\n      createdAt\n      lastLogin\n    }\n  }\n}",
        "variables": {
          "representations": [
            {
              "__typename": "Account",
              "id": "1"
            }
          ]
        },
       }
      }
    }
  ],

Using console logs I discovered that, even though the rule passes, the __resolveReference resolver never gets called, and the rule itself returns null, which causes the Cannot return null for non-nullable field Account.username. Here is the test I did:

Permissions

const test = rule()(
  async () => {
	  console.log('Rule is being executed')
	  return true
  }
)

const permissions = shield({
	Query: {
		// Some queries
	},
	Mutation: {
		// Some mutations
	},
	Account: test
},
{
	allowExternalErrors: true
})

Resolvers

Account: {
__resolveReference: async ({ id: accountId }, { models: { Accounts } }) => {
	console.log('Resolver is being executed')
	const { user, emails, phones } = await getAccount({ accountId }, { Accounts })
	return toAccountSchema({ ...user }, emails, phones)
    }
  },

The console logs showed that __resolveReference was never invoked after the rule.

Hey @shayzun 👋,

Thank you for opening an issue. We will get back to you as soon as we can. Have you seen our Open Collective page? Please consider contributing financially to our project. This will help us involve more contributors and get to issues like yours faster.

https://opencollective.com/graphql-shield

We offer priority support for all financial contributors. Don't forget to add priority label once you become one! 😄

This might be an issue with graphql-middleware. I am sorry, I can't help you any better. Hopefully, someone from the community can help you.

Thanks @maticzav. It seems like there is an issue already maticzav/graphql-middleware#351

@shayzun awesome! Let's close this one then in favour of that one so that we don't duplicate ourselves. Feel free to reopen the issue, however, if you think it might be beneficial.

Hello @maticzav! I am just learning the ropes on GraphQL + Federation + Shield and ran into this issue. Debugged through to figure out this was why my reference resolvers were not being called. The workaround posted in the referenced issue solved the problem for me: maticzav/graphql-middleware#351 (comment)

I don't know enough, though, to know if this is the intended behavior. Is it correct to apply the Shield middleware to the types and regular resolvers, and then add the reference resolvers outside of the Shield? Appreciate any insight you can share!