Support for Relay Modern

Hi!

I know this is a little old but I found a (admittedly very questionable) workaround for Assumption 1 (also part of the GraphQL specs) that a server must provide a root field called node which returns an interface Node. Doing this, will check 1 and 3 of the Relay compliance and pagination can also be done via first/next.

At first we have to add the Node interface to our schema and define a custom lambda resolver called node.

Node interface

interface Node @generate(
    query: {
        get: true,
        query: false,
        aggregate: false
    },
    mutation: {
        add: false,
        delete: false
    },
    subscription: false
) {
  id: ID!
}

I have disabled the generation of all queries/mutations apart from getNode.

Apply to a type

type Test implements Node {
  name: String
  ...
}

Apply the Node interface to any type you wish to query via node (@refetchable will work when using Relay).

Set up lambda

type Query {
  node(id: ID!): Node @lambda
}

So far so good, we have set all up but unfortunately the query node will not work since we would have to implement all possible query solutions which is offered by getNode. In order to do so I alter the id string of the input parameter and attach the query which gets generated by Relay (I know this is …).

I use react-relay-network-modern for setting up the Relay environment but it should work with the standard implementation as well. So when defining my network, I do:

const network = new RelayNetworkLayer([
    cacheMiddleware({
      size: 100, // max 100 requests
      ttl: 900000, // 15 minutes
    }),
    urlMiddleware({
      url: (req) => Promise.resolve(GRAPHQL_ENDPOINT),
    }),
    (next) => async (req) => {
      let queryBody = {
        query: req.getQueryString(),
        variables: req.getVariables(),
      };

      // THIS IS WHERE THE CRAZY HAPPENS
      if ( queryBody.query.includes('node(id: $id)') ) {
         queryBody.variables.id = queryBody.variables.id + '_QUERY_' + queryBody.query;
      }
 
      req.fetchOpts.body = JSON.stringify(queryBody);
      const res = await next(req);
      return res;
    },
  ]);

In the lambda I can now parse the string, replace node(id: $id) with getNode(id: $id) and run the query.

The lambda

export const getNode = async ({ args, graphql }) => {
  const { id } = args;
  const queryBody = id.split('_QUERY_');
  const queryId = queryBody[0];
  const query = query[1].replace('node(id: $id)', 'getNode(id: $id)');

  const res = await this.graphql( query, { id: queryId });
  return res.data.getNode;
}

I know this is a horrendous workaround but it works and lets me do @refetchables as well. I guess it would not be a major task for the Dgraph Team to automatically add the Node Interface and rename the getNode query to node. We would still have to implement Node to every type we would need to have access but that’s ok I guess.

Let me know what you think!

2 Likes