Dynamic (Unique) Objects in GraphQl
Asked Answered
S

3

19

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.

Scintillator answered 20/11, 2015 at 5:31 Comment(0)
T
12

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 },
    // ...
  },
});
Triplet answered 11/12, 2015 at 17:52 Comment(3)
I'm trying to use this, but I get this message: "message": "Argument \"data\" has invalid value {a: \"A\", b: \"B\"}.\nExpected type \"ObjectType\", found {a: \"A\", b: \"B\"}.". Any ideas where is the problem?Vaporescence
Seems you can now find this as a public module at npmjs.com/package/graphql-type-json.Maris
OMG The lib npmjs.com/package/graphql-type-json has over 1M weekly downloads and is depended up by 150k projects and still it has been released for the last time 2 years agoGrueling
G
11

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.

Giaour answered 9/12, 2015 at 18:30 Comment(1)
Hey, sorry for the late reply, that is a good solution indeed thank you. The problem is that there will not be just like box, sphere,cube,etc. We have to let it open to infinite possibilities =) but that is definitely a possibility. thank you!Scintillator
R
5

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

Revolution answered 30/11, 2018 at 12:15 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.