How to return an array of objects in GraphQL, possibly using the same endpoint as the one that returns a single object?

Haris Ghauri picture Haris Ghauri · Oct 12, 2018 · Viewed 17.9k times · Source

I am making a GraphQL API where I would be able to retrieve a car object by its id or retrieve all the cars when no parameter is provided.

Using the code below, I am successfully able to retrieve a single car object by supplying id as a parameter.

However, in the case where I would expect an array of objects i.e. when I supply no parameter at all, I get no result on GraphiQL.

schema.js

let cars = [
  { name: "Honda", id: "1" },
  { name: "Toyota", id: "2" },
  { name: "BMW", id: "3" }
];

const CarType = new GraphQLObjectType({
  name: "Car",
  fields: () => ({
    id: { type: GraphQLString },
    name: { type: GraphQLString }
  })
});

const RootQuery = new GraphQLObjectType({
  name: "RootQueryType",
  fields: {
    cars: {
      type: CarType,
      args: {
        id: { type: GraphQLString }
      },
      resolve(parent, args) {
        if (args.id) {
          console.log(cars.find(car => car.id == args.id));
          return cars.find(car => car.id == args.id);
        }
        console.log(cars);
        //***Problem Here***
        return cars;
      }
    }
  }
});

Test queries and their respective results:

Query 1

{
  cars(id:"1"){
    name
  }
}

Query 1 Response (Success)

{
  "data": {
    "cars": {
      "name": "Honda"
    }
  }
}

Query 2

{
  cars{
    name
  }
}

Query 2 Response (Fail)

{
  "data": {
    "cars": {
      "name": null
    }
  }
}

Any help would be much appreciated.

Answer

Daniel Rearden picture Daniel Rearden · Oct 12, 2018

A Car and a List of Cars are effectively two separate types. A field cannot resolve to a single Car object one time, and an array of Car object another.

Your query is returning null for the name because you told it the cars field would resolve to a single object, but it resolved to an array instead. As a result, it's looking for a property called name on the array object and since one doesn't exist, it's returning null.

You can handle this in a couple of different ways. To keep things to one query, you can use filter instead of find and change the type of your query to a List.

cars: {
  type: new GraphQLList(CarType), // note the change here
  args: {
    id: {
      type: GraphQLString
    },
  },
  resolve: (parent, args) => {
    if (args.id) {
      return cars.filter(car => car.id === args.id);
    }
    return cars;
  }
}

Alternatively, you could split this into two separate queries:

cars: {
  type: new GraphQLList(CarType),
  resolve: (parent, args) => cars,
},
car: {
  type: CarType,
  args: {
    id: {
      // example of using GraphQLNonNull to make the id required
      type: new GraphQLNonNull(GraphQLString)
    },
  },
  resolve: (parent, args) => cars.find(car => car.id === args.id),
}

Check the docs for more examples and options.