Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I add union type in Apollo graphql

I created this question in case anyone was curious on how to add union / Polymorphic types in Apollo. Hopefully this will make it easier for them.

In this example I wanted the response to either be a Worksheet or ApiError

// typedefs.js
export default [`
  schema {
    query: Query
  }

  type Query {
    worksheet(id: String!): Worksheet | Error
  }

  type Worksheet {
    id: String!
    name String
  }

  type ApiError {
    code: String!
    message: String!
  }
`];

// resolvers.js
export default {
  Query: {
    worksheet(_, args, { loaders }) {
      return loaders.worksheet.get(args.id).catch(() => {
        // ApiError
        return {
          code: '1',
          message: 'test'
        }
      });
    }
  }
};

// Express Server 
import { graphqlExpress } from 'apollo-server-express';
import { makeExecutableSchema } from 'graphql-tools';
import typeDefs from './typedefs';
import resolvers from './resolvers';

...


app.post(
  '/graphql',
  graphqlExpress(req => ({
    makeExecutableSchema({ typeDefs, resolvers }),
    context: mkRequestContext(req.ctx, req.log),
    formatError: formatGraphQLError(req.ctx, req.log)
  }))
);
like image 666
Kenneth Truong Avatar asked Oct 26 '25 03:10

Kenneth Truong


1 Answers

In GraphQL to add a union type in the typedefs you have to define the union

i.e union WorksheetOrError = Worksheet | ApiError

// typedefs.js
export default [
  `
  schema {
    query: Query
  }

  type Query {
    worksheet(id: String!): WorksheetOrError
  }

  union WorksheetOrError = Worksheet | ApiError 

  type Worksheet {
    id: String!
    name String
  }

  type ApiError {
    code: String!
    message: String!
  }
`];

In the resolvers you have to define a resolver for the union type that has the property __resolveType. This will help tell the GraphQL executor which type the result is.

 // resolvers.js
export default {
  Query: {
    worksheet() {
      ... 
    }
  },
  WorksheetOrError: {
    __resolveType(obj) {
      if (obj.id) {
        return 'Worksheet';
      }

      if (obj.code) {
        return 'ApiError';
      }

      return null;
    }
  },
};

To create a GraphQL Query in Apollo Client

// Your application code. 

// This is my Worksheet Query in my React code.
const WorksheetQuery = gql`
  query GetWorksheet($worksheetId: String!) {
    worksheet(id: $worksheetId) {
      ... on Worksheet {
        id
        name
      }
      ... on ApiError {
        code
        message
      }
    }
  }

Now you can check the __typename to check what type is in the response.

Note: For those who are wondering why I'm not using GraphQL errors. It's because Apollo doesn't seem to handle errors well when it encounters a graphQL error. So for a work around I'm trying to return a custom ApiError in my response.

There a few reasons why using a union with an error type is nice.

  1. Currently if you wanted a partial response with GraphQLError. Apollo does not cache the errors so if you wanted to re-use the cached response later you wouldn't have the complete response since the errors are removed. (Now you can't display the proper UI with errors)
  2. Getting GraphQLError back in Apollo would return a flat list of errors with the path to where the error is in the data. So you would need to verify that which part of your schema did the error occur in. However if you follow the instructions above you would have the error within the schema already. That way you already know which part of the schema the error happened.
like image 105
Kenneth Truong Avatar answered Oct 29 '25 09:10

Kenneth Truong



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!