Los fragmentos en el contexto de GraphQL pueden ser de dos tipos, definidos previamente o definidos en linea. Los primeros permiten simplificar las consultas definiendo en un bloque una colección de datos a recuperar si tener que indicarlos explícitamente individualmente, lo que resulta útil para no repetir el mimo grupo de datos si se utiliza en varias consultas diferentes. Los fragmentos en línea permiten recuperar unos datos u otros en función del tipo de la instancia de la cual se quieren recuperar.
Teniendo dos consultas que recuperan los datos de una colección de libros sin los fragmentos habría que definir los mismos datos a recuperar dos veces en ambas consultas. En estas consultas de ejemplo se recupera una lista de libros y un libro determinado. Si en ambas se recuperan los datos id, title y date hay que indicar los campos a recuperar dos veces.
1
2
3
4
5
6
7
8
9
10
11
12
|
query Books {
books {
id
title
date
}
book(id: "7") {
id
title
date
}
}
|
query-1.graphql
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
32
33
34
35
36
|
{
"data": {
"books": [
{
"id": "7",
"title": "Ojo en el cielo",
"date": "1957-01-01"
},
{
"id": "8",
"title": "Muerte de la luz",
"date": "1977-01-01"
},
{
"id": "9",
"title": "El nombre de la rosa",
"date": "1980-01-01"
},
{
"id": "10",
"title": "Los tejedores de cabellos",
"date": "1995-01-01"
},
{
"id": "11",
"title": "Ready Player One",
"date": "2011-01-01"
}
],
"book": {
"id": "7",
"title": "Ojo en el cielo",
"date": "1957-01-01"
}
}
}
|
query-1.json
Con un fragmento se definen esos campos comunes a recuperar en las consultas una sola vez. Si posteriormente cambian los datos a recuperar solo es necesario cambiarlo en un único punto. Los fragmentos definidos son una forma de simplificar las consultas y evitar tener que cambiar varias consultas si el grupo de datos cambia en todas ellas. Los datos obtenidos son los mismos que en el caso sin utilizar el fragmento.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
query Books {
books {
...data
}
book(id: "7") {
...data
}
}
fragment data on Book {
id
title
date
}
|
query-1-fragment.graphql
Los fragmentos en línea o inline permiten por otra parte una funcionalidad adicional y es recuperar diferentes datos según el tipo de la entidad. En el ejemplo he añadido una nueva entidad Magazine además de la ya existente Book, en el código Java ambas heredan de Publication. Las entidades Book y Magazine no comparten las mismas propiedades dado que son entidades diferentes por lo que en la consulta es necesario tener un mecanismo con el cual poder recuperar los datos en función del tipo.
Estas son las definiciones de las entidades resumidas y la consulta publications que devuelve las publicaciones que incluye libros y revistas. Con la definición de una union se establece que una Publication puede ser un Book o Magazine.
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
|
...
type Book {
id: Long
title: String
author: Author
isbn: String
date: LocalDate
...
}
type Magazine {
id: Long
name: String
pages: Long
}
...
union Publication = Book | Magazine
type Query {
books(filter: BookFilter): [Book]!
publications: [Publication]!
book(id: Long): Book!
authors: [Author]!
author(id: Long): Author!
}
...
|
library.graphqls
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
query Publications {
publications {
... on Book {
id
title
date
}
... on Magazine {
id
name
pages
}
}
}
|
fragment-inline.graphql
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
32
33
34
35
36
37
38
39
40
41
|
{
"data": {
"publications": [
{
"id": "7",
"title": "Ojo en el cielo",
"date": "1957-01-01"
},
{
"id": "8",
"title": "Muerte de la luz",
"date": "1977-01-01"
},
{
"id": "9",
"title": "El nombre de la rosa",
"date": "1980-01-01"
},
{
"id": "10",
"title": "Los tejedores de cabellos",
"date": "1995-01-01"
},
{
"id": "11",
"title": "Ready Player One",
"date": "2011-01-01"
},
{
"id": "12",
"name": "Muy interesante",
"pages": "65"
},
{
"id": "13",
"name": "PC Actual",
"pages": "90"
}
]
}
}
|
fragment-inline.json
Para las publicaciones del tipo Book en este ejemplo se recuperan los campos id, title y date. Para las publicaciones de tipo Magazine se recuperan los campos id, name y pages. Las publicaciones Muy interesante y PC Actual son dos Magazine y el resto de publicaciones son del tipo Book.
Si es necesario hay que añadir la clases Java que representan a los tipos de GraphQL a la lista de clases del diccionario en la definición del esquema.
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("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
Para cada entidad hay una clase Java que la representa y un repositorio que contiene la consulta para obtener las publicaciones que no hace más que añadir en una lista el conjunto de libros y revistas en la librería.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
package io.github.picodotdev.blogbitix.graphql.type;
import java.time.LocalDate;
import java.util.List;
public class Book extends Publication {
private Long id;
private String title;
private Author author;
private LocalDate date;
private List<Comment> comments;
public Book(Long id, String title, Author author, LocalDate date, List<Comment> comments) {
this.id = id;
this.title = title;
this.author = author;
this.date = date;
this.comments = comments;
}
...
}
|
Book.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
package io.github.picodotdev.blogbitix.graphql.type;
public class Magazine extends Publication {
private Long id;
private String name;
private Long pages;
public Magazine(Long id, String name, Long pages) {
this.id = id;
this.name = name;
this.pages = pages;
}
...
}
|
Magazine.java
1
2
3
4
5
|
package io.github.picodotdev.blogbitix.graphql.type;
public class Publication {
}
|
Publication.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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
|
package io.github.picodotdev.blogbitix.graphql.repository;
...
public class LibraryRepository {
private long sequence;
private List<Book> books;
private List<Comment> comments;
private List<Author> authors;
private List<Magazine> magazines;
public LibraryRepository() {
this.sequence = 0l;
this.books = new ArrayList<>();
this.comments = new ArrayList<>();
this.authors = new ArrayList<>();
this.magazines = new ArrayList<>();
...
this.books.addAll(
List.of(
new Book(nextId(), "Ojo en el cielo", a1, LocalDate.of(1957, 1, 1), this.comments),
new Book(nextId(), "Muerte de la luz", a2, LocalDate.of(1977, 1, 1), this.comments),
new Book(nextId(), "El nombre de la rosa", a3, LocalDate.of(1980, 1, 1), this.comments),
new Book(nextId(), "Los tejedores de cabellos", a4, LocalDate.of(1995, 1, 1), this.comments),
new Book(nextId(), "Ready Player One", a5, LocalDate.of(2011, 1, 1), this.comments)
)
);
this.magazines.addAll(
List.of(
new Magazine(nextId(), "Muy interesante", 65L),
new Magazine(nextId(), "PC Actual", 90L)
)
);
}
...
public List<Publication> findPublications() {
List<Publication> publications = new ArrayList<>();
publications.addAll(books);
publications.addAll(magazines);
return publications;
}
...
}
|
LibraryRepository.java
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