美文网首页graphql-java 手册
graphql-java使用手册: part2 创建Schema

graphql-java使用手册: part2 创建Schema

作者: MarkZhu | 来源:发表于2017-11-17 22:41 被阅读0次

    原文:http://blog.mygraphql.com/wordpress/?p=100

    创建Schema

    Schema的主要用途是定义所有可供查询的字段(field),它们最终组合成一套完整的GraphQL
    API.

    “graphql-java”提供两种方法来定义Schema。用java代码来定义、用GraphQL
    SDL(即IDL)来定义。

    注意:SDL(IDL)现在还不是 官方 graphql 规范. 本GraphQL实现,是基于
    已有的JS参考实现
    来开发的。但JS参考实现中的很多代码也是基于SDL(IDL)语法的,所以你可以认为这语法是可以长期使用的.

    如果你不确认用“java代码”还是用“GraphQL
    SDL(即IDL)”来定义你的Schema,那么我们建议你用SDL(IDL)

    SDL example:

    type Foo {
        bar: String
    }
    

    java代码例子:

    GraphQLObjectType fooType = newObject()
        .name("Foo")
        .field(newFieldDefinition()
                .name("bar")
                .type(GraphQLString))
        .build();
    

    DataFetcher 与 TypeResolver

    对象 DataFetcher
    作用是获取字段(field)对应的数据;另外,在修改(mutation)操作时,可以更新数据

    每个字段都有自己的 DataFetcher. 如果未为字段指定DataFetcher,
    那么自动使用默认的 PropertyDataFetcher .

    PropertyDataFetcherMap 和 Java Beans 中获取数据.
    所以,当Schema中的field名,与Map中的key值,或 Source Object 中的 java
    bean 字段名相同时,不需要为field指定 DataFetcher.

    对象 TypeResolver 帮助 graphql-java 判断数据的实际类型(type). 所以
    InterfaceUnion 均需要指定关联的 TypeResolver(类型识别器) .

    例如,你有一个 InterfaceMagicUserType
    它有可能是以下的具体类型(Type) Wizard, Witch and Necromancer.
    Type resolver(类型识别器) 的作用是在运行时识别出 GraphqlObjectType
    的具体类型(Type)。后期具体类型下的field相关的 data
    fetcher被调用并获取数据.

    new TypeResolver() {
        @Override
        public GraphQLObjectType getType(TypeResolutionEnvironment env) {
            Object javaObject = env.getObject();
            if (javaObject instanceof Wizard) {
                return (GraphQLObjectType) env.getSchema().getType("WizardType");
            } else if (javaObject instanceof Witch) {
                return (GraphQLObjectType) env.getSchema().getType("WitchType");
            } else {
                return (GraphQLObjectType) env.getSchema().getType("NecromancerType");
            }
        }
    };
    

    用 SDL 创建 Schema

    当使用SDL方法来开发时,你需要同时编写对应的 DataFetcher
    TypeResolver

    很大的 Schema IDL 文件很难查看。

    schema {
        query: QueryType
    }
    
    type QueryType {
        hero(episode: Episode): Character
        human(id : String) : Human
        droid(id: ID!): Droid
    }
    
    
    enum Episode {
        NEWHOPE
        EMPIRE
        JEDI
    }
    
    interface Character {
        id: ID!
        name: String!
        friends: [Character]
        appearsIn: [Episode]!
    }
    
    type Human implements Character {
        id: ID!
        name: String!
        friends: [Character]
        appearsIn: [Episode]!
        homePlanet: String
    }
    
    type Droid implements Character {
        id: ID!
        name: String!
        friends: [Character]
        appearsIn: [Episode]!
        primaryFunction: String
    }
    

    由于Schema中只是指定了静态的字段和类型,你还需要把它绑定到java方法中。以让Schema可以运行起来

    这里的绑定,包括 DataFetcher , TypeResolvers 与自定义 Scalar.

    用下页的Builder方法,就可以绑定Schema和Java程序

    RuntimeWiring buildRuntimeWiring() {
        return RuntimeWiring.newRuntimeWiring()
                .scalar(CustomScalar)
                // this uses builder function lambda syntax
                .type("QueryType", typeWiring -> typeWiring
                        .dataFetcher("hero", new StaticDataFetcher(StarWarsData.getArtoo()))
                        .dataFetcher("human", StarWarsData.getHumanDataFetcher())
                        .dataFetcher("droid", StarWarsData.getDroidDataFetcher())
                )
                .type("Human", typeWiring -> typeWiring
                        .dataFetcher("friends", StarWarsData.getFriendsDataFetcher())
                )
                // you can use builder syntax if you don't like the lambda syntax
                .type("Droid", typeWiring -> typeWiring
                        .dataFetcher("friends", StarWarsData.getFriendsDataFetcher())
                )
                // or full builder syntax if that takes your fancy
                .type(
                        newTypeWiring("Character")
                                .typeResolver(StarWarsData.getCharacterTypeResolver())
                                .build()
                )
                .build();
    }
    

    最后,你可以通过整合静态 Schema 和 绑定(wiring),而生成一个可以执行的
    Schema。

    SchemaParser schemaParser = new SchemaParser();
    SchemaGenerator schemaGenerator = new SchemaGenerator();
    
    File schemaFile = loadSchema("starWarsSchema.graphqls");
    
    TypeDefinitionRegistry typeRegistry = schemaParser.parse(schemaFile);
    RuntimeWiring wiring = buildRuntimeWiring();
    GraphQLSchema graphQLSchema = schemaGenerator.makeExecutableSchema(typeRegistry, wiring);
    

    除了上面的 builder 风格, TypeResolver s 与 DataFetcher s 也可以通过
    WiringFactory 接口绑定在一起。通过程序去分析 SDL
    ,就可以允许更自由的绑定。你可以 通过分析 SDL 声明, 或其它 SDL
    定义去决定你的运行时逻辑。

    RuntimeWiring buildDynamicRuntimeWiring() {
        WiringFactory dynamicWiringFactory = new WiringFactory() {
            @Override
            public boolean providesTypeResolver(TypeDefinitionRegistry registry, InterfaceTypeDefinition definition) {
                return getDirective(definition,"specialMarker") != null;
            }
    
            @Override
            public boolean providesTypeResolver(TypeDefinitionRegistry registry, UnionTypeDefinition definition) {
                return getDirective(definition,"specialMarker") != null;
            }
    
            @Override
            public TypeResolver getTypeResolver(TypeDefinitionRegistry registry, InterfaceTypeDefinition definition) {
                Directive directive  = getDirective(definition,"specialMarker");
                return createTypeResolver(definition,directive);
            }
    
            @Override
            public TypeResolver getTypeResolver(TypeDefinitionRegistry registry, UnionTypeDefinition definition) {
                Directive directive  = getDirective(definition,"specialMarker");
                return createTypeResolver(definition,directive);
            }
    
            @Override
            public boolean providesDataFetcher(TypeDefinitionRegistry registry, FieldDefinition definition) {
                return getDirective(definition,"dataFetcher") != null;
            }
    
            @Override
            public DataFetcher getDataFetcher(TypeDefinitionRegistry registry, FieldDefinition definition) {
                Directive directive = getDirective(definition, "dataFetcher");
                return createDataFetcher(definition,directive);
            }
        };
        return RuntimeWiring.newRuntimeWiring()
                .wiringFactory(dynamicWiringFactory).build();
    }
    

    用代码方式创建 schema

    如果用程序方式来定义 Schema,在创建类型(type)的时候,你需要提供
    DataFetcher and TypeResolver

    如:

    DataFetcher<Foo> fooDataFetcher = environment -> {
            // environment.getSource() is the value of the surrounding
            // object. In this case described by objectType
            Foo value = perhapsFromDatabase(); // Perhaps getting from a DB or whatever
            return value;
    }
    
    GraphQLObjectType objectType = newObject()
            .name("ObjectType")
            .field(newFieldDefinition()
                    .name("foo")
                    .type(GraphQLString)
                    .dataFetcher(fooDataFetcher))
            .build();
    

    类型(Types)

    GraphQL 类型系统支持以下类型

    • Scalar
    • Object
    • Interface
    • Union
    • InputObject
    • Enum

    Scalar

    graphql-java 支持以下基本数据类型( Scalars)。

    • GraphQLString
    • GraphQLBoolean
    • GraphQLInt
    • GraphQLFloat
    • GraphQLID
    • GraphQLLong
    • GraphQLShort
    • GraphQLByte
    • GraphQLFloat
    • GraphQLBigDecimal
    • GraphQLBigInteger

    Object

    SDL Example:

    type SimpsonCharacter {
        name: String
        mainCharacter: Boolean
    }
    

    Java 例子:

    GraphQLObjectType simpsonCharacter = newObject()
    .name("SimpsonCharacter")
    .description("A Simpson character")
    .field(newFieldDefinition()
            .name("name")
            .description("The name of the character.")
            .type(GraphQLString))
    .field(newFieldDefinition()
            .name("mainCharacter")
            .description("One of the main Simpson characters?")
            .type(GraphQLBoolean))
    .build();
    

    Interface

    Interfaces 是抽象的 类型( types)定义.

    SDL Example:

    interface ComicCharacter {
        name: String;
    }
    

    Java 例子:

    GraphQLInterfaceType comicCharacter = newInterface()
        .name("ComicCharacter")
        .description("An abstract comic character.")
        .field(newFieldDefinition()
                .name("name")
                .description("The name of the character.")
                .type(GraphQLString))
        .build();
    

    Union

    SDL Example:

    interface Cat {
        name: String;
        lives: Int;
    }
    
    interface Dog {
        name: String;
        bonesOwned: int;
    }
    
    union Pet = Cat | Dog
    

    Java 例子:

    GraphQLUnionType PetType = newUnionType()
        .name("Pet")
        .possibleType(CatType)
        .possibleType(DogType)
        .typeResolver(new TypeResolver() {
            @Override
            public GraphQLObjectType getType(TypeResolutionEnvironment env) {
                if (env.getObject() instanceof Cat) {
                    return CatType;
                }
                if (env.getObject() instanceof Dog) {
                    return DogType;
                }
                return null;
            }
        })
        .build();
    

    Enum

    SDL Example:

    enum Color {
        RED
        GREEN
        BLUE
    }
    

    Java 例子:

    GraphQLEnumType colorEnum = newEnum()
        .name("Color")
        .description("Supported colors.")
        .value("RED")
        .value("GREEN")
        .value("BLUE")
        .build();
    

    ObjectInputType

    SDL Example:

    input Character {
        name: String
    }
    

    Java 例子:

    GraphQLInputObjectType inputObjectType = newInputObject()
        .name("inputObjectType")
        .field(newInputObjectField()
                .name("field")
                .type(GraphQLString))
        .build();
    

    类型引用 (Type References) (递归类型recursive types)

    GraphQL 支持递归类型:如 Person(人)
    可以包含很多朋友【译注:当然这些也是人类型的】

    为了方便声明这种情况, graphql-java 有一个 GraphQLTypeReference 类。

    在实际的 Schema 创建时,GraphQLTypeReference 会变为实际的类型。

    例如:

    GraphQLObjectType person = newObject()
        .name("Person")
        .field(newFieldDefinition()
                .name("friends")
                .type(new GraphQLList(new GraphQLTypeReference("Person"))))
        .build();
    

    如果用SDL(ID L)来定义 Schema ,不需要特殊的处理。

    Schema IDL的模块化

    很大的 Schema IDL 文件很难查看。所以我们有两种方法可以模块化 Schema。

    方法一是合并多个 Schema IDL 文件到一个逻辑单元( logic
    unit)。下面的例子是,在 Schema 生成前,合并多个独立的文件。

    SchemaParser schemaParser = new SchemaParser();
    SchemaGenerator schemaGenerator = new SchemaGenerator();
    
    File schemaFile1 = loadSchema("starWarsSchemaPart1.graphqls");
    File schemaFile2 = loadSchema("starWarsSchemaPart2.graphqls");
    File schemaFile3 = loadSchema("starWarsSchemaPart3.graphqls");
    
    TypeDefinitionRegistry typeRegistry = new TypeDefinitionRegistry();
    
    // each registry is merged into the main registry
    typeRegistry.merge(schemaParser.parse(schemaFile1));
    typeRegistry.merge(schemaParser.parse(schemaFile2));
    typeRegistry.merge(schemaParser.parse(schemaFile3));
    
    GraphQLSchema graphQLSchema = schemaGenerator.makeExecutableSchema(typeRegistry, buildRuntimeWiring());
    

    Graphql IDL 还有其它方法去做模块化。你可以使用 type extensions
    去为现有类型增加字段和 interface。

    例如,一开始,你有这样一个文件:

    type Human {
        id: ID!
        name: String!
    }
    

    你的系统的其它模块可以扩展这个类型:

    extend type Human implements Character {
        id: ID!
        name: String!
        friends: [Character]
        appearsIn: [Episode]!
    }
    

    你可以按你的需要去扩展。它们会以被发现的顺序组合起来。重复的字段会被合并(但重定义一个字段的类型是不允许的)。

    extend type Human {
        homePlanet: String
    }
    

    完成合并后的 Human 类型会是这样的:

    type Human implements Character {
        id: ID!
        name: String!
        friends: [Character]
        appearsIn: [Episode]!
        homePlanet: String
    }
    

    这在顶层查询中特别有用。你可以用 extension types 去为顶层 “query”
    增加字段每个团队可以提供自己的字段集,进而合成完整的查询。

    schema {
      query: CombinedQueryFromMultipleTeams
    }
    
    type CombinedQueryFromMultipleTeams {
        createdTimestamp: String
    }
    
    # maybe the invoicing system team puts in this set of attributes
    extend type CombinedQueryFromMultipleTeams {
        invoicing: Invoicing
    }
    
    # and the billing system team puts in this set of attributes
    extend type CombinedQueryFromMultipleTeams {
        billing: Billing
    }
    
    # and so and so forth
    extend type CombinedQueryFromMultipleTeams {
        auditing: Auditing
    }
    

    Subscription(订阅)的支持

    订阅功能还未在规范中: graphql-java 现在只支持简单的实现 ,你可以用
    GraphQLSchema.Builder.subscription(...) 在 Schema
    中定义订阅。这使你可以处理订阅请求。

    subscription foo {
        # normal graphql query
    }
    

    相关文章

      网友评论

        本文标题:graphql-java使用手册: part2 创建Schema

        本文链接:https://www.haomeiwen.com/subject/ouysvxtx.html