SOFA icon indicating copy to clipboard operation
SOFA copied to clipboard

Relay support

Open ezsper opened this issue 5 years ago • 0 comments

Sofa is very opinionated on what it considers a model, it's a good thing to consider the Relay Design Pattern for Node, we don't need to have a method to query for each model, we simply have a node method that query all types that implements the Node interface.

interface Node {
  id: ID!
}

type User implements Node {
  id: ID!
  displayName: String!
}

type UserEdge {
  cursor: String!
  node: User!
}

type PageInfo {
  startCursor: String!
  endCursor: String!
  hasNextPage: Boolean!
  hasPrevPage: Boolean!
}

type UserConnection {
  edges: [UserEdge!]!
  nodes: [User!]!
  info: PageInfo!
}

type Query {
  node(id: ID!): Node!
  userConnection(
    first: Int
    last: Int
    after: String
    before: String  
  ): UserConnection!
}

So my workaround while SOFA doesn't support this officially is the following:

import { useSofa, createSofa } from 'sofa-api';
import { wrapSchema, FilterObjectFields } from '@graphql-tools/wrap';

const nodes = schema.getImplementations(schema.getType('Node') as any)
      .objects.map(({ name }) => name);

const sofaIgnore: string[] = [];

const sofaSchema = wrapSchema(graphSchema, [
    new FilterObjectFields(
      (typeName, fieldName) => {
        if (!typeName) {
           return true;
        }
        if (typeName.endsWith('Connection') && fieldName === 'nodes') {
           // it will omit nodes, only listing edges for all connections
           return false;
        }
        if (typeName.endsWith('Edge') && fieldName === 'node') {
          // it will ignore and let a node inside an edge to retrieve full object node
          sofaIgnore.push(`${typeName}.node`);
        }
        return true;
      },
    ),
]);

const sofa = createSofa({
  schema: sofaSchema,
  ignore: sofaIgnore,
});


// Include nodes as models
sofa.models = Array.from(new Set(sofa.models.concat(nodes)));

app.use('/rest', useSofa(sofa));

The only problema that still remains that the workaround wont fix is the Node interface or interfaces in general, since Node only has the id field it only returns the id, interface should be spreading implemented object fields in the root level like:

query FetchNode ($id: ID!) {
   node(id: $id) {
      __typename
      id
      ...on User {
         displayName
      }
   }
}

Sure we can overwrite this with the execute option inside Sofa, but would be great if we could come with a better solution for Relay without the need to create a method for each model (node).

ezsper avatar Oct 07 '20 11:10 ezsper