MicroProfile GraphQL  acaba de lanzar su primera versión (1.0). En esta publicación de blog, exploraremos algunas de las funcionalidades disponibles en esta versión.

Haremos referencia a una aplicación de ejemplo, donde usamos  Thorntail  como tiempo de ejecución, agregando manualmente la  implementación de SmallRye GraphQL .

¿Qué es GraphQL?

“GraphQL es un lenguaje de manipulación y consulta de datos de código abierto para API, y un tiempo de ejecución para completar consultas con datos existentes. GraphQL interpreta cadenas del cliente y devuelve datos de una manera predefinida, predecible y comprensible. GraphQL es una alternativa, aunque no necesariamente un reemplazo de REST ".

Lea la especificación GraphQL completa 

¿Qué es MicroProfile GraphQL?

“La intención de la especificación MicroProfile GraphQL es proporcionar un conjunto de API de" código primero "que permitirá a los usuarios desarrollar rápidamente aplicaciones portátiles basadas en GraphQL en Java. Hay 2 requisitos principales para todas las implementaciones de esta especificación, a saber:

  • Genere y ponga a disposición el esquema GraphQL. Esto se hace mirando las anotaciones en el código de los usuarios y debe incluir todas las consultas y mutaciones de GraphQL, así como todas las entidades definidas implícitamente a través del tipo de respuesta o argumento (s) de consultas y mutaciones.
  • Ejecute solicitudes GraphQL. Esto tendrá la forma de una consulta o una mutación. Como mínimo, la especificación debe admitir la ejecución de estas solicitudes a través de HTTP ".

Lea la especificación completa de  MicroProfile GraphQL

El ejemplo

Haremos referencia a un ejemplo a lo largo de esta publicación en el que tenemos un sistema que puntúa a las personas en función de algunas actividades que realizan.

  • Qué tan en forma están, ejemplo con qué frecuencia van al gimnasio
  • Qué tan seguros conducen, sin exceder el límite de velocidad, etc.
  • Cuántos pasos dan al día, etc.

Tenemos un objeto Persona que contiene todos los detalles sobre la persona (nombre, apellido, dirección, teléfono, redes sociales, etc.) y tenemos un objeto de puntuación que contiene la puntuación de una determinada acción.

Consultar datos

En GraphQL,  Query es similar a REST  GETUna consulta no cambia datos, solo recibe datos. La diferencia es que con GraphQL puedes preguntar lo que quieras.

Usando JAX-RS

Digamos que quieres información sobre una persona, en JAX-RS crearíamos algo como esto:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
@Path("/person")
@Consumes(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)
@Tag(name = "Person service",description = "Person Services")
public class PersonRestApi {
 
    @Inject
    private PersonDB personDB;
 
    @GET
    @Path("/{id}")
    @Operation(description = "Get a person using the person's Id")
    public Response getPerson(@PathParam("id") int id){
        return Response.ok(personDB.getPerson(id)).build();
    }
}

NOTA: Para documentar el punto final, también necesita  MicroProfile OpenAPI  ( @Tag y  @Operation).

Puede obtener el esquema en  /openapi, que describirá los servicios y modelos disponibles.

Hacer un GET en  /person/1 devolverá la Persona en formato json.

Dependiendo del tamaño de Person, este puede ser un archivo grande:

001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
{
  "addresses": [
    {
      "code": "49512-5971",
      "lines": [
        "212 Neomi Ridges",
        "Lake Winfordview",
        "Wisconsin",
        "Myanmar"
      ]
    },
    {
      "code": "09444-8413",
      "lines": [
        "1443 Bogan Harbor",
        "East Jacinto",
        "New York",
        "Timor-Leste"
      ]
    }
  ],
  "biography": "Perferendis asperiores non. Cumque voluptas et nisi rerum tenetur quaerat. Doloribus nihil et. Autem autem sapiente et ut inventore ipsum sint. Aut error pariatur quidem itaque deserunt. Dolorum aspernatur exercitationem. In tenetur quia iste qui quia officiis.",
  "birthDate": "25/01/1967",
  "coverphotos": [
  ],
  "creditCards": [
    {
      "expiry": "2013-9-12",
      "number": "1234-2121-1221-1211",
      "type": "solo"
    }
  ],
  "emailAddresses": [
    "leo.lesch@gmail.com",
    "laurie.ankunding@gmail.com"
  ],
  "favColor": "lime",
  "gender": "Female",
  "id": 1,
  "idNumber": "334-58-1049",
  "imClients": [
    {
      "identifier": "Slack",
      "im": "cory.langworth"
    },
    {
      "identifier": "ICQ",
      "im": "booker.boyle"
    }
  ],
  "interests": [
    "PUBG",
    "meditation"
  ],
  "joinDate": "14/09/2019",
  "locale": "en-ZA",
  "maritalStatus": "Divorced",
  "names": [
    "Nicholas",
    "Edwin"
  ],
  "nicknames": [
    "Oscar Ruitt"
  ],
  "occupation": "Officer",
  "organization": "Hodkiewicz Group",
  "phoneNumbers": [
    {
      "number": "1-852-821-3630",
      "type": "Cell"
    },
    {
      "number": "783.874.6411",
      "type": "Home"
    },
    {
      "number": "(489) 031-5336 x8428",
      "type": "Work"
    }
  ],
  "profilePictures": [
  ],
  "relations": [
    {
      "personURI": "/rest/person/46",
      "relationType": "Spouse"
    }
  ],
  "skills": [
    "Communication",
    "Confidence"
  ],
  "socialMedias": [
    {
      "name": "@jordan.hane",
      "username": "Twitter"
    },
    {
      "name": "annamarie.casper",
      "username": "Facebook"
    }
  ],
  "surname": "Zulauf",
  "taglines": [
    "You will find only what you bring in.",
    "Chuck Norris' keyboard doesn't have a F1 key, the computer asks him for help."
  ],
  "title": "Dr.",
  "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 12_0_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/12.0 Mobile/15E148 Safari/604.1",
  "username": "trent.kub",
}

Ahora bien, si está creando una aplicación móvil, o incluso si la consume de otro servicio, es posible que solo le importe el  name,  surname y  idNumber como ejemplo, pero está recuperando una gran cantidad de datos. Ahora necesita filtrar toda la información irrelevante (para usted).

Usando MicroProfile GraphQL

Ahora agreguemos MicroProfile GraphQL a esto y veamos cómo podemos consultar los datos:

Primero agregue esto a su pom.xml (al menos hasta que el tiempo de ejecución lo admita como una funcionalidad lista para usar):

1
2
3
4
5
<dependency>
    <groupId>io.smallrye</groupId>
    <artifactId>smallrye-graphql-servlet</artifactId>
    <version>1.0.0</version>
</dependency>

Ahora podemos agregar un punto final GraphQL (similar a nuestro servicio REST anterior)

01
02
03
04
05
06
07
08
09
10
11
12
@GraphQLApi
public class ProfileGraphQLApi {
     
    @Inject
    private PersonDB personDB;
 
    @Query
    @Description("Get a person using the person's Id")
    public Person getPerson(@Name("personId") int personId){
        return personDB.getPerson(personId);
    }
}

Como puede ver, el código es exactamente el mismo que el del servicio REST, pero tenemos algunas anotaciones nuevas:

  • @GraphQLApi esto le dice al tiempo de ejecución que este es un punto final GraphQL, y todos los métodos anotados con  @Query y  @Mutation serán expuestos.
  • @Query este es un método consultable.

Ahora puede realizar  POST una consulta a  /graphqlUsemos el mismo ejemplo anterior, desea obtener el  name,  surname y  idNumber de la persona:

1
2
3
4
5
6
7
{
  person(personId:1){
    names
    surname
    idNumber
  }
}

Esto devolverá exactamente lo que solicitó:

01
02
03
04
05
06
07
08
09
10
11
12
{
  "data": {
    "person": {
      "names": [
        "Nicholas",
        "Edwin"
      ],
      "surname": "Zulauf",
      "idNumber": "334-58-1049"
    }
  }
}

y como puede ver, la carga útil es mucho menor que en el ejemplo REST.

Acabamos de resolver el problema de la búsqueda excesiva:

"Recuperar en exceso es obtener demasiados datos, por lo que hay datos en la respuesta que no usa".

Ahora digamos que también queremos obtener las puntuaciones de esta persona. En el ejemplo de REST, necesitamos obtener el  idNumber de nuestro enorme json original y luego realizar una llamada posterior al servicio de puntuación:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
dieciséis
{@ApplicationScoped
@Path("/score")
@Consumes(MediaType.APPLICATION_JSON) @Produces