2017-08-09 4 views
4

Kann man angeben, dass ein Feld in GraphQL eine Blackbox sein soll, ähnlich wie Flow einen "Any" -Typ hat? Ich habe ein Feld in meinem Schema, das in der Lage sein sollte, jeden beliebigen Wert zu akzeptieren, der ein String, Boolean, Object, Array usw. sein könnte.GraphQL Blackbox/"Any" -Typ?

Antwort

3

Ja. Erstellen Sie einfach einen neuen GraphQLScalarType, der alles erlaubt.

Hier ist ein Ich schrieb, dass Objekte erlaubt. Sie können es etwas erweitern, um mehr Root-Typen zuzulassen.

import {GraphQLScalarType} from 'graphql'; 
import {Kind} from 'graphql/language'; 
import {log} from '../debug'; 
import Json5 from 'json5'; 

export default new GraphQLScalarType({ 
    name: "Object", 
    description: "Represents an arbitrary object.", 
    parseValue: toObject, 
    serialize: toObject, 
    parseLiteral(ast) { 
     switch(ast.kind) { 
      case Kind.STRING: 
       return ast.value.charAt(0) === '{' ? Json5.parse(ast.value) : null; 
      case Kind.OBJECT: 
       return parseObject(ast); 
     } 
     return null; 
    } 
}); 

function toObject(value) { 
    if(typeof value === 'object') { 
     return value; 
    } 
    if(typeof value === 'string' && value.charAt(0) === '{') { 
     return Json5.parse(value); 
    } 
    return null; 
} 

function parseObject(ast) { 
    const value = Object.create(null); 
    ast.fields.forEach((field) => { 
     value[field.name.value] = parseAst(field.value); 
    }); 
    return value; 
} 

function parseAst(ast) { 
    switch (ast.kind) { 
     case Kind.STRING: 
     case Kind.BOOLEAN: 
      return ast.value; 
     case Kind.INT: 
     case Kind.FLOAT: 
      return parseFloat(ast.value); 
     case Kind.OBJECT: 
      return parseObject(ast); 
     case Kind.LIST: 
      return ast.values.map(parseAst); 
     default: 
      return null; 
    } 
} 
3

Ich habe eine mittlere Lösung gefunden. Statt zu versuchen, diese Komplexität auf GraphQL zu übertragen, verwende ich einfach die Daten String und JSON.stringify meine Daten, bevor Sie sie auf dem Feld einstellen. Also wird alles stringifiziert, und später in meiner Anwendung, wenn ich dieses Feld konsumieren muss, JSON.parse das Ergebnis, um das gewünschte Objekt/Array/Boolean/etc. zurück zu bekommen

0

@ mpens Antwort ist großartig, aber ich entschied mich für eine kompaktere Lösung:

const { GraphQLScalarType } = require('graphql') 
const { Kind } = require('graphql/language') 

const ObjectScalarType = new GraphQLScalarType({ 
    name: 'Object', 
    description: 'Arbitrary object', 
    parseValue: (value) => { 
    return typeof value === 'object' ? value 
     : typeof value === 'string' ? JSON.parse(value) 
     : null 
    }, 
    serialize: (value) => { 
    return typeof value === 'object' ? value 
     : typeof value === 'string' ? JSON.parse(value) 
     : null 
    }, 
    parseLiteral: (ast) => { 
    switch (ast.kind) { 
     case Kind.STRING: return JSON.parse(ast.value) 
     case Kind.OBJECT: throw new Error(`Not sure what to do with OBJECT for ObjectScalarType`) 
     default: return null 
    } 
    } 
}) 

Dann sieht mein Resolvern wie:

{ 
    Object: ObjectScalarType, 
    RootQuery: ... 
    RootMutation: ... 
} 

Und mein .gql wie folgt aussieht:

scalar Object 

type Foo { 
    id: ID! 
    values: Object! 
} 
Verwandte Themen