Ti sei mai chiesto se c’è un modo migliore per recuperare i dati per le tue applicazioni rispetto alle API REST? Nello sviluppo back-end, GraphQL è emerso come un’alternativa potente, offrendo un approccio più flessibile ed efficiente al recupero dei dati. Per gli sviluppatori familiari con Java, l’integrazione di GraphQL in un backend moderno apre la porta a API scalabili e ad alte prestazioni adatte a una vasta gamma di casi d’uso.
Questo blog esplorerà le principali differenze tra GraphQL e REST, evidenzierà i vantaggi unici dell’uso di GraphQL per il recupero dei dati e ti guiderà nell’implementazione di un’API GraphQL in Java con un esempio concreto.
Cos’è GraphQL?
GraphQL è un linguaggio di query per le API e un runtime per l’esecuzione di tali query. A differenza di REST, dove i punti di accesso fissi restituiscono dati predefiniti, GraphQL consente ai client di richiedere esattamente i dati di cui hanno bisogno. Questa granularità rende GraphQL altamente efficiente, specialmente per applicazioni complesse o ad elevato consumo di dati.
Vantaggi dell’Approccio GraphQL:
- Recupero Granulare dei Dati: Il client può interrogare solo nome e ruolo senza recuperare campi non necessari come il dipartimento.
- Query Nidificate: Recupera i dettagli del manager insieme alle informazioni dell’impiegato in una singola query.
- Sviluppo Basato su Schema: Lo schema funge da contratto, facilitando l’evoluzione dell’API.
Cos’è un’API REST?
Transferimento di Stato Rappresentazionale (REST) è uno stile architettonico per la costruzione di API. Utilizza metodi HTTP standard come GET, POST, PUT e DELETE per eseguire operazioni CRUD. REST è conosciuto per la sua semplicità e per la sua ampia adozione.
Limitazioni di REST:
- Recupero eccessivo o insufficiente di dati.
- Richiede più endpoint e versioning per adattarsi ai cambiamenti.
- Non ha capacità di real-time integrate.
GraphQL vs. REST API: Qual è la differenza?
GraphQL e REST sono due approcci popolari per la costruzione di API, ognuno con i propri punti di forza. Mentre REST è stato lo standard per anni, GraphQL offre maggiore flessibilità ed efficienza, in particolare nel recupero dei dati e nella collaborazione tra team front-end e back-end.
Principali differenze
- A differenza di REST, che utilizza più endpoint e richiede versioning per i cambiamenti, GraphQL consolida il recupero dei dati in un’unica query e riduce la necessità di versioning poiché i client specificano i requisiti dei dati.
- Mentre REST utilizza codici di stato HTTP per indicare successi o errori, GraphQL restituisce sempre uno stato 200 OK e comunica gli errori nel corpo della risposta.
- GraphQL supporta anche aggiornamenti in tempo reale tramite abbonamenti, a differenza di REST, che manca di supporto integrato per il real-time.
- Sebbene REST sia ampiamente consolidato con molti strumenti, l’ambiente di GraphQL è cresciuto rapidamente, offrendo strumenti potenti come GraphiQL per uno sviluppo più semplice.
- Infine, mentre REST utilizza intestazioni per la cache, GraphQL richiede tecniche più avanzate a causa delle query dinamiche, ma offre opzioni come le query persistenti per una cache efficiente.
Concetti fondamentali di GraphQL
1. Linguaggio di definizione dello schema (SDL)
GraphQL ha il proprio sistema di tipi che viene utilizzato per definire lo schema di un’API. La sintassi per scrivere schemi è chiamata Linguaggio di definizione dello schema (SDL).
2. Query vs. Mutazioni vs. Sottoscrizioni
- Le query vengono utilizzate per recuperare dati dal server. A differenza di REST, che utilizza più endpoint fissi, GraphQL utilizza un unico endpoint, e il client specifica i dati necessari nella query, offrendo flessibilità.
- Le mutazioni vengono utilizzate per modificare i dati sul server, come creare, aggiornare o eliminare dati. Consentono ai client di inviare modifiche al backend ed sono essenziali per le applicazioni che devono scrivere dati.
- Le sottoscrizioni abilitano aggiornamenti in tempo reale mantenendo una connessione costante tra il client e il server. Quando si verifica un evento a cui si è iscritti, il server invia aggiornamenti al client, fornendo flussi di dati continui, a differenza delle query e delle mutazioni, che seguono un ciclo di richiesta-risposta.
3. Schema GraphQL
Definisce la struttura dei dati che può essere interrogata o modificata, fungendo da contratto tra il server e il client. Specifica i tipi, i campi e le relazioni disponibili per l’accesso dei client. Lo schema include tipicamente tipi radice speciali: Query per il recupero dei dati, Mutation per modificare i dati e Subscription per aggiornamenti in tempo reale. Questi tipi definiscono collettivamente le capacità dell’API e come i client possono interagire con essa.
4. Risolutori: Mappatura delle Query GraphQL ai Dati
I risolutori sono funzioni che gestiscono la logica per il recupero dei dati in un server GraphQL. Ogni campo in uno schema è collegato a un risolutore, che determina come recuperare o calcolare i dati per quel campo. Quando una query viene eseguita, il server invoca i risolutori appropriati per i campi richiesti. I risolutori possono restituire scalari o oggetti, continuando l’esecuzione per i campi figli se viene restituito un oggetto e completando se viene restituito uno scalare. Se viene restituito null, l’esecuzione si ferma. I risolutori sono essenziali per mappare le query GraphQL alle effettive fonti di dati.
Vantaggi dell’Utilizzo di GraphQL in Java
- Recupero Dati Esatto: Interroga solo i dati di cui hai bisogno, nient’altro, garantendo risultati prevedibili ed efficienti.
- Richiesta Unica per Risorse Multiple: Recupera dati correlati in una sola query, riducendo le chiamate API multiple.
- Sistema di Tipi: Organizza le API per tipi e campi, garantendo che le query siano valide e che gli errori siano chiari.
- Strumenti per Sviluppatori: Aumenta la produttività con strumenti come GraphiQL, utilizzando definizioni di tipo per una migliore costruzione e debug delle query.
- Evoluzione senza versione: Aggiungi o depreca campi senza interrompere le query esistenti, mantenendo le API gestibili.
- Integrazione dati flessibile: Crea un’API unificata su dati e codice esistenti compatibile con vari motori di archiviazione e linguaggi.
Configurazione di un’API GraphQL in Java
Esempio concreto: Utenti e Ordini
Immagina di dover creare un’API per una directory aziendale di una grande organizzazione. L’obiettivo è consentire ai clienti di interrogare dettagli come nome dell’impiegato, designazione, dipartimento e persino la loro gerarchia di reportistica.
1. Imposta il Progetto
Crea un nuovo progetto Spring Boot utilizzando Spring Tool Suite oppure recandoti su Spring Initialiser. Quindi, aggiungi queste dipendenze al file 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. Crea le Tue Entità
Crea entità Java (ad esempio, Utente
e Ordine
) per rappresentare i dati che verranno interrogati o modificati tramite GraphQL. Ad esempio:
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. Crea i Repository
Crea dei repository per interagire con il database:
public interface UserRepository extends JpaRepository<User, Long> {}
public interface OrderRepository extends JpaRepository<Order, Long> {}
4. Crea Classi di Servizio
Creare classi di servizio per gestire la logica di business:
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. Creare controller GraphQL
Definire controller GraphQL per gestire le query e le mutazioni:
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. Definire lo schema GraphQL
Creare un file schema.graphqls
nella directory 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. Configurare GraphQL in application.properties
Opzionalmente, configurare le impostazioni di GraphQL in scr/main/resources/application.properties
:
spring.graphql.graphiql.enabled=true
8. Eseguire l’applicazione
Eseguire l’applicazione SpringBoot utilizzando mvn spring-boot:run
o dal tuo IDE. Una volta in esecuzione, puoi accedere al punto di accesso GraphAL a /graphiql
.
9. Testare con le query GraphQL
Testare l’API GraphQL utilizzando uno strumento come GraphiQL o Postman.
Per le mutazioni:
mutation {
createUser(
name:"swetha",
email:"[email protected]",
password:"23sde4dfg43"
){
name,
userId
}
}
Output:
{
"data": {
"createUser": {
"name": "swetha",
"userId": "3"
}
}
}
Per le query:
query{
getUsers{
name
}
}
Output:
{
"data": {
"getUsers": [
{
"name": "Medha"
},
{
"name": "Riya"
},
{
"name": "swetha"
}
]
}
}
Funzionalità avanzate di GraphQL
1. Migliorare la riutilizzabilità con i frammenti
Un frammento è essenzialmente un insieme riutilizzabile di campi definiti per un tipo specifico. È una funzionalità che aiuta a migliorare la struttura e la riutilizzabilità del tuo codice GraphQL.
2. Parametrizzare i campi con gli argomenti
In GraphQL, i campi possono accettare argomenti per rendere le query più dinamiche e flessibili. Questi argomenti ti permettono di filtrare o personalizzare i dati restituiti dall’API.
3. Paginazione e Ordinamento con GraphQL
Paginazione
La paginazione è un argomento complesso nel design delle API. A un livello più alto, ci sono due approcci principali su come affrontarla.
- Limit-offset: Richiedi un pezzo specifico della lista fornendo gli indici degli elementi da recuperare (infatti, fornisci principalmente l’indice di inizio (offset) e un conteggio degli elementi da recuperare (limit)).
- Cursor-based: Questo modello di paginazione è un po’ più avanzato. Ogni elemento nella lista è associato a un ID univoco (il cursore). I client che paginano attraverso la lista forniscono quindi il cursore dell’elemento di inizio e un conteggio degli elementi da recuperare.
Ordinamento
Con il design delle API GraphQL, è possibile restituire liste di elementi ordinati (ordered) secondo criteri specifici.
Sfide e Considerazioni nell’Uso di GraphQL
- Complessità: Gestire gli schemi e le query GraphQL può essere impegnativo per modelli di dati semplici o team inesperti.
- Problemi di Prestazioni: Le query profondamente nidificate possono mettere sotto pressione le risorse del server se non ottimizzate.
- Sfide nella Cache: Le strategie di caching standard basate su REST non si applicano e richiedono soluzioni personalizzate.
- Preoccupazioni di sicurezza: Il sovraccarico e le query dannose richiedono limiti di query e altre protezioni.
- Utilizzo ibrido: Funziona meglio per esigenze di dati complessi, spesso combinato con REST per operazioni più semplici.
Conclusione
GraphQL offre un approccio flessibile ed efficiente per la costruzione di API moderne in Java, rendendolo una scelta ideale per applicazioni dinamiche e ad alto contenuto di dati. La sua architettura a singolo punto di accesso e la tipizzazione forte semplificano la progettazione delle API garantendo al contempo un’ottima performance. Che tu stia creando un semplice elenco dipendenti o una piattaforma di analisi complessa, GraphQL consente ai programmatori di sviluppare soluzioni scalabili con facilità. Inizia a esplorare GraphQL oggi con strumenti come Spring Boot e graphql-java
per sbloccarne tutto il potenziale nel tuo prossimo progetto.
Codice sorgente
Puoi trovare il codice sorgente completo di questo tutorial su Github.
Source:
https://dzone.com/articles/design-scalable-java-apis-with-graphql