2

I am exploring graphql and the question that is troubling me is if i can do some kind of type composition when defining my graphql server.

Let's assume that I have Person and Company in my collection. I having something like this:

const Person = new GraphQLObjectType({
  name: 'Person',
  fields: {
    firstName: {type: GraphQLString},
    lastName: {type: GraphQLString}
  }
});

const Company = new GraphQLObjectType({
  name: 'Company',
  fields: {
    name: {type: GraphQLString},
    website: {type: GraphQLString}
  }
});

But both Company and Person should have fields like: createdAt and id. So assume i have:

const Entity = new GraphQLObjectType({
  name: 'Entity',
  fields: {
    id: {type: GraphQLString},
    createdAt: {type: GraphQLString}
  }
});

So i was thinking about something like:

new GraphQLObjectType({
  ...
  extends: [Entity]
});

I know that there are interfaces but i think it is not what i am looking for, because then i need to implement interface anyway and what i want to achive is to keep some set of field definitions sepratly and reuse them across other types.

Any ideas? Am i trying to do something completely pointless?

jano
  • 735
  • 7
  • 20

2 Answers2

0

If we look at the GraphQL language spec there are interfaces. Interfaces are used to describe common behaviour in two types. Interfaces usually only make sense if you return two subtypes within the same field. The GraphQL documentation has a nice example on this. I would advise against using an interface in your case unless you want to return all entity types in the same field (like in a search).

You are talking about the server implementation level. There is no way that I know of to extend types like this in GraphQL.js. What you could do though is create a JavaScript object that contains both of your fields. You can then reuse this code and insert it into every type for example using Object.assign:

  const standardFields = {
    id: {
      type: new GraphQLNonNull(GraphQLID),
      description: 'Unique id for this entity'
    },
    createdAt: {
      type: new GraphQLNonNull(GraphQLString),
      description: 'Datetime of this entity\'s creation'
    }
  }

  const Company = new GraphQLObjectType({
    name: 'Company',
    fields: Object.assign({}, standardFields, {
      name: {type: GraphQLString},
      website: {type: GraphQLString}
    }
  });

Or maybe import the fields from a file and insert them explicitly:

  const { id, createdAt } = require('./standardFields');

  const Company = new GraphQLObjectType({
    name: 'Company',
    fields: {
      id,
      createdAt,
      name: {type: GraphQLString},
      website: {type: GraphQLString}
    }
  });

In both cases I don't see to much gain from this. It might be more useful to set up a test that ensures that all types contain the fields.

Herku
  • 7,198
  • 27
  • 36
  • That is exactly what i am doing at the moment but i do not really like this solution because in this case `id` and `createdAt` are not self-contained. – jano May 08 '17 at 18:41
  • This is probably a typical "inheritance or composition" question. What every pattern you are looking for, it is not represented in the actual API. So we would have to ask what is the point of both anyways. Avoiding code duplication? Testability? Why are you looking for inheritance here if not only for the sake of it? I would not worry about it too much, in our app we actually repeat the fields explicitly. This gives us an easily understandable code with explicit dependencies. – Herku May 09 '17 at 11:23
  • Key part: "Interfaces usually only make sense if you return two subtypes within the same field." GraphQL does allow [type *extension*](https://stackoverflow.com/questions/48917863/in-graphql-how-do-you-do-basic-extend-types/56204966#56204966) though. – Dan Dascalescu May 19 '19 at 05:17
0

You can get the fields from an interface type like this:

const carInterface = new GraphQLInterfaceType({
  name: 'car',
  fields: {
    _id: { type: GraphQLID },
    userId: { type: GraphQLID },
    carType: { type: new GraphQLNonNull(GraphQLString), description: 'نوع اعلان السيارة' },
    title: { type: new GraphQLNonNull(GraphQLString), description: 'عنوان الاعلان' },
    brand: { type: GraphQLString, description: 'الماركة-النوع' },
    model: { type: GraphQLString, description: 'الموديل' }
   }
});
console.log(carInterface._typeConfig.fields);

You can easily add carInterface._typeConfig.fields to any GraphQLObject fields definition;

Shalkam
  • 733
  • 6
  • 12