¿Alguna vez te has preguntado si hay una mejor manera de obtener datos para tus aplicaciones que no sea a través de las API REST? En el desarrollo de back-end, GraphQL ha surgido como una alternativa poderosa, ofreciendo un enfoque más flexible y eficiente para la obtención de datos. Para los desarrolladores familiarizados con Java, la integración de GraphQL en un back-end moderno abre las puertas a APIs escalables y de alto rendimiento diseñadas para una amplia gama de casos de uso.
Este blog explorará las diferencias clave entre GraphQL y REST, destacará los beneficios únicos de utilizar GraphQL para la obtención de datos, y te guiará a través de la implementación de una API GraphQL en Java con un ejemplo del mundo real.
¿Qué es GraphQL?
GraphQL es un lenguaje de consulta para APIs y un entorno de ejecución para esas consultas. A diferencia de REST, donde los puntos finales fijos devuelven datos predefinidos, GraphQL permite a los clientes solicitar exactamente los datos que necesitan. Esta granularidad hace que GraphQL sea altamente eficiente, especialmente para aplicaciones complejas o intensivas en datos.
Ventajas del enfoque de GraphQL:
- Obtención de datos granular: El cliente puede consultar solo el nombre y la designación sin recuperar campos innecesarios como el departamento.
- Consultas anidadas: Obtén los detalles del gerente junto con la información del empleado en una sola consulta.
- Desarrollo basado en esquemas: El esquema actúa como un contrato, facilitando la evolución de la API.
¿Qué es una API REST?
Representational State Transfer (REST) es un estilo arquitectónico para construir APIs. Utiliza métodos estándar de HTTP como GET, POST, PUT y DELETE para realizar operaciones CRUD. REST es conocido por su simplicidad y amplia adopción.
Limitaciones de REST:
- Sobre o sub-solicitud de datos.
- Requiere múltiples puntos finales y versionado para acomodar cambios.
- Sin capacidades integradas en tiempo real.
GraphQL vs. REST API: ¿Cuál es la Diferencia?
GraphQL y REST son dos enfoques populares para construir APIs, cada uno con sus fortalezas. Mientras que REST ha sido el estándar durante años, GraphQL ofrece más flexibilidad y eficiencia, especialmente en la recuperación de datos y la colaboración entre equipos de front-end y back-end.
Diferencias Clave
- A diferencia de REST, que utiliza múltiples puntos finales y requiere versionado para cambios, GraphQL consolida la recuperación de datos en una sola consulta y reduce la necesidad de versionado ya que los clientes especifican los requisitos de datos.
- Mientras que REST utiliza códigos de estado de HTTP para indicar éxito o errores, GraphQL siempre devuelve un estado 200 OK y comunica errores en el cuerpo de la respuesta.
- GraphQL también soporta actualizaciones en tiempo real a través de suscripciones, a diferencia de REST, que carece de soporte integrado en tiempo real.
- Aunque REST está ampliamente establecido con muchas herramientas, el entorno de GraphQL ha crecido rápidamente, ofreciendo herramientas poderosas como GraphiQL para un desarrollo más fácil.
- Por último, mientras que REST utiliza encabezados para el almacenamiento en caché, GraphQL requiere técnicas más avanzadas debido a las consultas dinámicas, pero ofrece opciones como consultas persistentes para un almacenamiento en caché eficiente.
Conceptos básicos de GraphQL
1. Lenguaje de Definición de Esquema (SDL)
GraphQL tiene su propio sistema de tipos que se utiliza para definir el esquema de una API. La sintaxis para escribir esquemas se llama Lenguaje de Definición de Esquema (SDL).
2. Consultas vs. Mutaciones vs. Suscripciones
- Consultas se utilizan para obtener datos del servidor. A diferencia de REST, que utiliza múltiples puntos finales fijos, GraphQL utiliza un solo punto final, y el cliente especifica los datos necesarios en la consulta, ofreciendo flexibilidad.
- Mutaciones se utilizan para modificar datos en el servidor, como crear, actualizar o eliminar datos. Permiten a los clientes enviar cambios al backend y son esenciales para aplicaciones que necesitan escribir datos.
- Suscripciones permiten actualizaciones en tiempo real al mantener una conexión constante entre el cliente y el servidor. Cuando ocurre un evento suscrito, el servidor envía actualizaciones al cliente, proporcionando flujos de datos continuos, a diferencia de las consultas y mutaciones, que siguen un ciclo de solicitud-respuesta.
3. Esquema de GraphQL
Define la estructura de datos que puede ser consultada o modificada, actuando como un contrato entre el servidor y el cliente. Especifica los tipos, campos y relaciones disponibles para que los clientes accedan. El esquema típicamente incluye tipos raíz especiales: Query para recuperación de datos, Mutation para modificar datos y Subscription para actualizaciones en tiempo real. Estos tipos definen colectivamente las capacidades de la API y cómo los clientes pueden interactuar con ella.
4. Resolutores: Mapeo de Consultas GraphQL a Datos
Los resolutores son funciones que manejan la lógica para obtener datos en un servidor GraphQL. Cada campo en un esquema está vinculado a un resolutor, que determina cómo recuperar o calcular los datos para ese campo. Cuando se ejecuta una consulta, el servidor invoca los resolutores apropiados para los campos solicitados. Los resolutores pueden devolver escalares u objetos, con la ejecución continuando para campos secundarios si se devuelve un objeto y completando si se devuelve un escalar. Si se devuelve nulo, la ejecución se detiene. Los resolutores son esenciales para mapear consultas GraphQL a las fuentes de datos reales.
Beneficios de Usar GraphQL en Java
- Recuperación Exacta de Datos: Consulta solo los datos que necesitas, nada más, asegurando resultados predecibles y eficientes.
- Solicitud Única para Múltiples Recursos: Obtén datos relacionados en una sola consulta, reduciendo múltiples llamadas a la API.
- Sistema de Tipos: Organiza las APIs por tipos y campos, asegurando que las consultas sean válidas y los errores sean claros.
- Herramientas para Desarrolladores: Mejora la productividad con herramientas como GraphiQL, utilizando definiciones de tipos para una mejor construcción y depuración de consultas.
- Evolución sin versión: Agregar o deprecar campos sin romper consultas existentes, manteniendo APIs mantenibles.
- Integración de datos flexible: Crear una API unificada sobre datos y código existentes que sea compatible con varios motores de almacenamiento y lenguajes.
Configuración de una API de GraphQL en Java
Ejemplo del mundo real: Usuarios y Pedidos
Imagina que estás construyendo una API de Directorio de Empleados para una gran organización. El objetivo es permitir a los clientes consultar detalles como el nombre del empleado, cargo, departamento e incluso su jerarquía de reporte.
1. Configurar el Proyecto
Crea un nuevo proyecto Spring Boot usando Spring Tool Suite o visitando Spring Initialiser. Luego, agrega estas dependencias al archivo pom.xml
:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-graphql</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webflux</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.graphql</groupId>
<artifactId>spring-graphql-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
2. Crear tus Entidades
Crea entidades Java (por ejemplo, Usuario
y Pedido
) para representar los datos que serán consultados o modificados a través de GraphQL. Por ejemplo:
public class User {
strategy = GenerationType.IDENTITY) (
private Long userId;
private String name;
private String email;
private String password;
// Getters and setters...
}
public class Order {
strategy = GenerationType.IDENTITY) (
private Long orderId;
private String orderDetails;
private String address;
private int price;
private User user;
// Getters and setters...
}
3. Crear Repositorios
Crea repositorios para interactuar con la base de datos:
public interface UserRepository extends JpaRepository<User, Long> {}
public interface OrderRepository extends JpaRepository<Order, Long> {}
4. Crear Clases de Servicio
Crear clases de servicio para manejar la lógica de negocio:
public class UserService {
private final UserRepository userRepository;
public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}
public User createUser(User user) {
return userRepository.save(user);
}
public User getUser(Long userId) {
return userRepository.findById(userId).orElseThrow(() -> new RuntimeException("User not found"));
}
public List<User> getAllUsers() {
return userRepository.findAll();
}
public boolean deleteUser(Long userId) {
userRepository.deleteById(userId);
return true;
}
}
5. Crear controladores de GraphQL
Definir controladores de GraphQL para manejar consultas y mutaciones:
public class UserController {
private final UserService userService;
public UserController(UserService userService) {
this.userService = userService;
}
public List<User> getUsers() {
return userService.getAllUsers();
}
public User getUser( Long userId) {
return userService.getUser(userId);
}
public User createUser( String name, String email, String password) {
User user = new User();
user.setName(name);
user.setEmail(email);
user.setPassword(password);
return userService.createUser(user);
}
public boolean deleteUser( Long userId) {
return userService.deleteUser(userId);
}
}
6. Definir el esquema de GraphQL
Crear un archivo schema.graphqls
en el directorio src/main/resources
:
type User {
userId: ID!
name: String
email: String
password: String
}
type Query {
getUsers: [User]
getUser(userId: ID!): User
}
type Mutation {
createUser(name: String, email: String, password: String): User
deleteUser(userId: ID!): Boolean
}
7. Configurar GraphQL en application.properties
Opcionalmente, configurar las opciones de GraphQL en scr/main/resources/application.properties
:
spring.graphql.graphiql.enabled=true
8. Ejecutar la aplicación
Ejecutar la aplicación SpringBoot usando mvn spring-boot:run
o desde tu IDE. Una vez en ejecución, puedes acceder al punto final de GraphAL en /graphiql
.
9. Probar con consultas de GraphQL
Probar la API de GraphQL usando una herramienta como GraphiQL o Postman.
Para la mutación:
mutation {
createUser(
name:"swetha",
email:"[email protected]",
password:"23sde4dfg43"
){
name,
userId
}
}
Resultado:
{
"data": {
"createUser": {
"name": "swetha",
"userId": "3"
}
}
}
Para la consulta:
query{
getUsers{
name
}
}
Resultado:
{
"data": {
"getUsers": [
{
"name": "Medha"
},
{
"name": "Riya"
},
{
"name": "swetha"
}
]
}
}
Funciones avanzadas de GraphQL
1. Mejorando la reusabilidad con fragmentos
Un fragmento es básicamente un conjunto reutilizable de campos definidos para un tipo específico. Es una característica que ayuda a mejorar la estructura y reusabilidad de tu código de GraphQL.
2. Parametrizando campos con argumentos
En GraphQL, los campos pueden aceptar argumentos para hacer las consultas más dinámicas y flexibles. Estos argumentos te permiten filtrar o personalizar los datos devueltos por la API.
3. Paginación y Ordenación con GraphQL
Paginación
La paginación es un tema complicado en el diseño de API. En un nivel alto, hay dos enfoques principales sobre cómo puede abordarse.
- Límite-desplazamiento: Solicita un fragmento específico de la lista proporcionando los índices de los elementos a ser recuperados (de hecho, principalmente estás proporcionando el índice de inicio (desplazamiento) así como un recuento de elementos a ser recuperados (límite)).
- Basado en cursor: Este modelo de paginación es un poco más avanzado. Cada elemento en la lista está asociado con un ID único (el cursor). Los clientes que están paginando a través de la lista luego proporcionan el cursor del elemento de inicio así como un recuento de elementos a ser recuperados.
Ordenación
Con el Diseño de API de GraphQL, es posible devolver listas de elementos que están ordenados según criterios específicos.
Desafíos y Consideraciones al Usar GraphQL
- Complejidad: Gestionar esquemas y consultas de GraphQL puede ser desafiante para modelos de datos simples o equipos inexpertos.
- Problemas de Rendimiento: Las consultas profundamente anidadas pueden sobrecargar recursos del servidor si no están optimizadas.
- Desafíos de Caché: Las estrategias estándar de caché basadas en REST no se aplican y requieren soluciones personalizadas.
- Preocupaciones de seguridad: El exceso de consultas y las consultas maliciosas requieren límites de consulta y otras medidas de protección.
- Uso híbrido: Funciona mejor para necesidades de datos complejas, a menudo combinado con REST para operaciones más simples.
Conclusión
GraphQL ofrece un enfoque flexible y eficiente para construir API modernas en Java, lo que lo convierte en una elección ideal para aplicaciones dinámicas y con gran cantidad de datos. Su arquitectura de un solo punto final y su tipado fuerte simplifican el diseño de la API y garantizan un rendimiento sólido. Ya sea que estés creando un simple directorio de empleados o una plataforma de análisis compleja, GraphQL permite a los desarrolladores ofrecer soluciones escalables con facilidad. Comienza a explorar GraphQL hoy mismo con herramientas como Spring Boot y graphql-java
para desbloquear todo su potencial en tu próximo proyecto.
Código fuente
Puedes encontrar el código fuente completo de este tutorial en Github.
Source:
https://dzone.com/articles/design-scalable-java-apis-with-graphql