Search code examples
apollostackapollo-server

Apollo GraphQL: Schema for Query to Return Objects of Different Types?


I have three different PostGres tables, each containing different types of associates. The database fields are different for each type-- that's why they are in three separate tables.

I have one component that can potentially access any type of associate. Now it seems from the examples I've encountered so far, that a component is typically associated with one GraphQL query, e.g.:

const withData = graphql(GETONEASSOCIATE_QUERY, {
    options({ navID }) {
        return {
            variables: { _id: navID}
        };
    }
    ,
    props({ data: { loading, getOneAssociate } }) {
        return { loading, getOneAssociate };
    },


});

export default compose(
    withData,
    withApollo
)(AssociatesList);

And it appears that a given GraphQL query, can only return a single type of record, e.g. in schema:

getOneAssociate(associateType: String): [associateAccountingType]

Question: is it possible to design a GraphQL schema such that a single query, can return objects of different types? The resolver could receive an associateType parameter that would tell it what postGres table to reference. But what would the schema look like, so that it could return objects of type associateAccountingType, associateArtDirectorType, associateAccountExecType, etc. as needed?

Thanks in advance to all for any info.


Solution

  • You have two options here. Declare an Interface as the returned type and make sure each of these associateTypes extends that interface. This is a good idea in cases where you have common fields on all of those types together. It will look like so:

    interface associateType {
      id: ID
      department: Department
      employees: [Employee]
    }
    
    type associateAccountingType implements associateType {
      id: ID
      department: Department
      employees: [Employee]
      accounts: [Account]
    }
    
    type associateArtDirectorType implements associateType {
      id: ID
      department: Department
      employees: [Employee]
      projects: [Project]
    }
    

    If you don't have any common fields or you'd rather have those types unrelated for some reason, you can use union type. this declaration is much simpler but requires you will use a fragment for each field you query as the engine assumes these types have no common fields.

    union associateType = associateAccountingType | associateArtDirectorType | associateAccountExecType
    

    One more important thing will be how to implement a resolver that will tell your graphql server and your client what is the actual concrete type. For apollo, you need to supply the __resolveType function on union/interace type:

    {
      associateType: {
        __resolveType(associate, context, info) {
          return associate.isAccounting ? 'associateAccountingType' : 'associateArtDirectorType';
        },
      }
    },
    

    This function can implement whatever logic you want but it must return the name of the type you are using. associate argument will be the actual object you returned from the parent resolver. context is your usual context object and info holds the query and schema information.