Создание сервера Relay GraphQL с помощью graphql-java

Мы используем graphql-java для создания сервера graphql и испытываем проблемы с соответствием спецификации Relay. Согласно спецификации Relay, все узлы должны быть извлечены с помощью одного запроса:node(id: ID). Документация graphql-java показывает поддержку интерфейса узла Relay, однако документация по его реальной реализации довольно скудна. Точная проблема, с которой мы сталкиваемся, заключается в понимании того, как создать общий поиск для запроса узла? Пример Relay Todo: https://github.com/graphql-java/todomvc-relay-java показывает чрезвычайно простой подход, основанный на коде, с одним сборщиком данных, в данном примере никогда не требуется "читать" типы узлов или делегируйте этот запрос правильному dataFetcher.

schema.graphqls:

type Query {
    node(id: ID!): Node
    user(id: ID!): User
    otherType(id:ID!): OtherType
}

interface Node {
   id: ID!
}

type User implements Node {
   id: ID!
   firstName: String
   lastName: String
}

type OtherType implements Node {
   id: ID!
   description: String
   stuff: String
}

В настоящее время мы используем SDL для создания нашей схемы (как показано ниже)

@Bean
private GraphQLSchema schema() {
     Url url = Resources.getResource("schema.graphqls");
     String sdl = Resources.toString(url, Charsets.UTF_8);
     GraphQLSchema graphQLSchema = buildSchema(sdl);
     this.graphQL = GraphQL.newGraphQL(graphQLSchema).build();
     return graphQLSchema;
}
private GraphQLSchema buildSchema(String sdl) {
      TypeDefinitionRegistry typeRegistry - new SchemaParser().parse(sdl);
      RuntimeWiring runtimeWiring = buildWiring();
      SchemaGenerator schemaGenerator = new SchemaGenerator();
      return schemaGenerator.makeExecutableSchema(typeRegistry, runtimeWiring);
}
private RuntimeWiring buildWiring(){
    return RuntimeWiring.newRuntimeWiring()
    .type(newTypeWiring("Query")
          .dataFetcher("user", userDispatcher.getUserById()))
    .type(newTypeWiring("Query")
          .dataFetcher("otherType", otherTypeDispatcher.getOtherTypeById()))
    .type(newTypeWiring("Query")
          .dataFetcher("node", /*How do we implement this data fetcher? */))
    .build()
}

Предположительно, нам придется подключить сборщик данных, как показано в последней строке выше. Однако здесь документация начинает расплываться. Соответствует ли указанное выше спецификации реле? Как бы мы могли реализовать один узел dataFetcher, который возвращает ссылку на любой отдельный узел, независимо от базового типа (User, OtherType и т. Д.)?

2 ответа

Интерфейс узла в схеме нуждается в TypeResolver, а не в реализации сборщика данных.

Даже если вы предоставите реализацию сборщика данных и подключите ее, как это предлагает AllirionX, вам все равно необходимо предоставить преобразователь для интерфейса Node, чтобы помочь graphql-java понять, какой конкретный тип объекта он должен разрешать.

      .type(newTypeWiring("Query").dataFetcher("node", new NodeDataFetcher()))

Что-то вроде этого -

      .type(newTypeWiring("Node").typeResolver(new TypeResolver() {
                @Override
                public GraphQLObjectType getType(TypeResolutionEnvironment env) {
                    return null;
                }
            }))

Вы правы, вам нужно реализовать сборщик данных узла, который будет возвращать узел из идентификатора узла.

Я предполагаю, что вы используете базовую базу данных sql: узел может быть любого типа, а это означает, что вы не можете использовать идентификатор базы данных в качестве идентификатора узла (потому что два узла разных типов могут иметь один и тот же идентификатор базы данных). Вам нужно создать свой собственный идентификатор узла.

Простой способ создать такой идентификатор - объединить тип объекта и идентификатор объекта. Пример: "Пользователь:4234".

Как указано в спецификации сервера relay graphql:

Идентификаторы, которые мы получили, были строками в формате base64. Идентификаторы спроектированы так, чтобы быть непрозрачными (единственное, что должно быть передано в аргумент id на узле, - это неизмененный результат запроса идентификатора для некоторого объекта в системе), а использование строки base64 является полезным соглашением в GraphQL, чтобы напомнить зрителям, что строка - непрозрачный идентификатор.

Нам нужно кодировать / декодировать идентификатор узла до базы 64.

Приведенный ниже код не был протестирован, но он даст вам представление о том, как действовать.

DataFetcher dataFetcher = new DataFetcher() {
            
  @Override
  Object get(DataFetchingEnvironment environment) {
    Object argument = environment.getArgument("id");
    if(argument instanceof String) {
      String node = (String) argument;
      //TODO decode base 64 nodeId
      //TODO split nodeId into String `nodeType` and Long `id`
      //TODO retrieve your object of type `nodeType` and id `id`
      
    }
  }
}
Другие вопросы по тегам