Dynamic (Unique) Objects in GraphQl

2019-03-18 09:02发布

问题:

I'm looking at graphql. Is it possible to define an object with arbitrary attributes? Let's say I have some data like:

editOptions : { boxes : 3 , size : { width: 23,height:32} , color: #434343 }, etc...}

and this is in:

{ ... , box : { editOptions : {...} }, ... }

Let's say that editOptions is never with the same structure, sometimes not be useful to have the color, just for example sakes. In mongoose one can just define the type to something like:

editOptions : {}

These editOptions are usually unique for each box. With some attributes being shared but most being unique.

So my question is, is there a way to do this? or is this bad practice and I should change my models.

Thank you.

回答1:

Use GraphQLScalarType, simply implement it like:

import { GraphQLScalarType } from 'graphql/type';
import { GraphQLError } from 'graphql/error';
import { Kind } from 'graphql/language';

const ObjectType = new GraphQLScalarType({
  name: 'ObjectType',
  serialize: value => value,
  parseValue: value => value,
  parseLiteral: (ast) => {
    if (ast.kind !== Kind.OBJECT) {
      throw new GraphQLError(
        `Query error: Can only parse object but got a: ${ast.kind}`, 
        [ast],
      );
    }
    return ast.value;
  },
});

const ReturnType = new GraphQLObjectType({
  name: 'ReturnType',
  fields: {
    // ...
    editOptions: { type: ObjectType },
    // ...
  },
});


回答2:

You have two options.

1. Interface

If the editOptions may vary based on the type, but are consistent for that particular type, you can use an Interface (node.js example).

Let's say you have two objects, a box and a sphere. You can define an object interface that both implement:

interface Object
type Box implements Object {
  editOptions: BoxOptions
}
type BoxOptions {
  boxes: Int,
  size: ...,
  color: ...
}
type Sphere implements Object {
  editOptions: SphereOptions
}
type SphereOptions {
  spheres: Int,
  ...
}
type Query {
  objects: [Object]
}

In your query, you'd then return an Object, and the requested options based for each type:

query Query {
  objects(filter: "...") {
    ... on Box {
      editOptions {
        boxes
        size
      }
    }
    ... on Sphere {
      editOptions {
        spheres
      }
    }
  }
}

In the returned JSON, boxes will have boxes and size fields under editOptions, and spheres will have spheres.

sometimes not be useful to have the color

If for some of the boxes you don't have the color, the field would simply be empty (but still exist in the schema).

2. JSON

If the editOptions can be really variable, you can just define the field as String, and send over serialized JSON. You'll lose all the type validations but the structure can be totally arbitrary for each object. Just make sure your client understands what to do with it.



回答3:

Try JSON scalar type for GraphQL.js: graphql-type-json. It works great for me.

When using the SDL with GraphQL-tools, define GraphQLJSON as the resolver for the appropriate scalar type in your schema:

import { makeExecutableSchema } from 'graphql-tools';
import GraphQLJSON from 'graphql-type-json';

const typeDefs = `
  scalar JSON

  type MyType {
    editOptions: JSON
  }
`;

const resolvers = {
  JSON: GraphQLJSON,
};

export default makeExecutableSchema({ typeDefs, resolvers });

You can also use this in a programmatically-constructed schema.

See for more details on this package