Usar un resolver para recuperar propiedades en GraphQL

Escrito por el , actualizado el .
java planeta-codigo programacion
Enlace permanente Comentarios

GraphQL

Cuando en una consulta de GraphQL se indican las propiedades a devolver GraphQL usa para cada una de ellas lo que en la implementación de Java se llama un resolver y en otras implementaciones data fetcher. En la mayoría de casos las propiedades serán propiedades de un objeto y en estos casos se usará un PropertyDataFetcher usando en Java la convención de los java beans o la clave de un mapa. En el caso de que cierto dato no esté almacenado en el objeto sino en un repositorio externo es necesario usar un resolver para devolver esa propiedad en la consulta.

Por ejemplo, supongamos que en el ejemplo de la librería en el caso de los libros le añadimos un nuevo dato para el ISBN que está almacenado en un sistema externo, en otro repositorio. La nueva definición del esquema quedaría de la siguiente forma, basta con añadir la nueva propiedad al tipo Book y su tipo que será String.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
scalar Long

type Book {
    id: Long
    title: String
    author: Author
    isbn: String
}

type Author {
    id: Long
    name: String
}

type Query {
    books: [Book]!
    authors: [Author]!
    author(id: Long): Author!
}

type Mutation {
    addBook(title: String, author: Long): Book
}

schema {
    query: Query
    mutation: Mutation
}
library.graphqls

Para que una consulta que recupere el ISBN funcione correctamente es necesario implementar un resolver creando una clase que implemente la interfaz GraphQLResolver<Book> en la que se incluya un método get por cada propiedad del tipo Book que esté almacenada en otro repositorio. Estos métodos get reciben como parámetro el objeto Book a partir del cual como contexto es posible tener los datos del libro del que hay que recuperar el ISBN, posiblemente utilizando su identificativo. En el ejemplo simplemente se devuelve un dato aleatorio pero perfectamente en caso necesario se podría usar un repositorio que lo recupere del sistema de información que lo almacena.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
package io.github.picodotdev.blogbitix.graphql.resolver;

...

public class BookResolver implements GraphQLResolver<Book> {

    private LibraryRepository libraryRespository;

    public BookResolver(LibraryRepository libraryRespository) {
        this.libraryRespository = libraryRespository;
    }

    public String getIsbn(Book book) throws InterruptedException {
        System.out.printf("Getting ISBN %d...", book.getId());
        Thread.sleep(3000);
        System.out.printf("ok%n");
        return UUID.randomUUID().toString();
    }

    ...
}
BookResolver.java
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
$ curl -XPOST -H "Content-Type: application/json" -d '{"query": "query Books{books{title isbn}}"}' http://localhost:8080/graphql
{
  "data": {
    "books": [
      {
        "title": "Ojo en el cielo",
        "isbn": "9664c44a-9b70-4f8e-8db9-960c16bd3d9c"
      },
      {
        "title": "Muerte de la luz",
        "isbn": "b2cadd23-827c-4057-ae65-e3fbcbafee03"
      },
      {
        "title": "El nombre de la rosa",
        "isbn": "57ccc845-8792-40ed-9561-d75070399c8b"
      },
      {
        "title": "Los tejedores de cabellos",
        "isbn": "f035727e-8a7d-4a34-9604-1f5b22c249a7"
      },
      {
        "title": "Ready Player One",
        "isbn": "40393df6-7bfb-4132-97d4-bd98ebbfd565"
      }
    ]
  }
}
curl.sh

A la hora de definir el servicio de GraphQL hay que proporcionar el resolver personalizado.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
package io.github.picodotdev.blogbitix.graphql;

...

@SpringBootApplication
public class Main {

    public static final Logger logger = LoggerFactory.getLogger(Main.class);

    @Bean
    public LibraryRepository buildLibraryRepository() {
        return new LibraryRepository();
    }

    @Bean
    public GraphQLSchema graphQLSchema(LibraryRepository libraryRepository) throws IOException {
        return SchemaParser.newParser()
                .schemaString(IOUtils.resourceToString("/library.graphqls", Charset.forName("UTF-8")))
                .resolvers(new Query(libraryRepository), new Mutation(libraryRepository), new BookResolver(libraryRepository), new MagazineResolver(libraryRepository))
                .scalars(GraphQLScalarType.newScalar().name("Long").description("Long scalar").coercing(new LongCoercing()).build(), GraphQLScalarType.newScalar().name("LocalDate").description("LocalDate scalar").coercing(new LocalDateCoercing()).build())
                .dictionary(Magazine.class)
                .build()
                .makeExecutableSchema();
    }

    ...

    public static void main(String[] args) {
        SpringApplication.run(Main.class, args);
    }
}
Main.java

Los resolvers permiten almacenar la información en dos bases de datos distintas, una podría ser almacenar una información una base de datos relacional, otra información en una base de datos NoSQL, dos bases de datos relacionales distintas o incluso proporcionado por una API distinta. En cualquier caso para el usuario de la API y del servicio es transparente como esté almacenada la información.

Terminal

El código fuente completo del ejemplo puedes descargarlo del repositorio de ejemplos de Blog Bitix alojado en GitHub y probarlo en tu equipo ejecutando siguiente comando:
./gradlew run


Comparte el artículo: