Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use unions with GraphQL buildSchema

Here is how I am using a GraphQL schema string to create a schema and attach it to my Express server:

var graphql = require('graphql');
var graphqlHTTP = require('express-graphql');
[...]
    return graphqlHTTP({
      schema: graphql.buildSchema(schemaText),
      rootValue: resolvers,
      graphiql: true,
    });

This is all very basic use of the modules. It works well and is quite convenient until I want to define a union:

union MediaContents = Photo|Youtube

type Media {
  Id: String
  Type: String
  Contents: MediaContents
}

I have found no way to make this work, querying Contents does what it has to do, returns the correct object but fails with the message Generated Schema cannot use Interface or Union types for execution.

Is it at all possible to use unions when using buildSchema ?

like image 524
jérôme Muffat-Méridol Avatar asked Dec 14 '22 22:12

jérôme Muffat-Méridol


2 Answers

That's exactly why we created the graphql-tools package, which is like a production-ready, supercharged version of buildSchema: http://dev.apollodata.com/tools/graphql-tools/resolvers.html#Unions-and-interfaces

You can simply use unions by providing a __resolveType method on the union, as usual with GraphQL.js:

# Schema
union Vehicle = Airplane | Car

type Airplane {
  wingspan: Int
}

type Car {
  licensePlate: String
}

// Resolvers
const resolverMap = {
  Vehicle: {
    __resolveType(obj, context, info){
      if(obj.wingspan){
        return 'Airplane';
      }
      if(obj.licensePlate){
        return 'Car';
      }
      return null;
    },
  },
};

The only change is, instead of providing your resolvers as the root object, use makeExecutableSchema:

const graphqlTools = require('graphql-tools');
return graphqlHTTP({
  schema: graphqlTools.makeExecutableSchema({
    typeDefs: schemaText,
    resolvers: resolvers
  }),
  graphiql: true,
});

Also note that the signature of the resolvers will match the regular GraphQL.js style, so it's going to be (root, args, context) instead of just (args, context) which is what you get when you use the rootValue.

like image 76
stubailo Avatar answered Jan 27 '23 21:01

stubailo


In case you are returning an object with all the information, you can add a __typename field in your object. Like this:

return {
  token: res.token,
  user: {
    __typename: 'YOUR_TYPE_HERE',
    ...res.user
  }
};
like image 29
Tahnik Mustasin Avatar answered Jan 27 '23 22:01

Tahnik Mustasin