Haben Sie sich jemals gefragt, ob es einen besseren Weg gibt, Daten für Ihre Anwendungen abzurufen als REST-APIs? In der Backend-Entwicklung hat sich GraphQL als kraftvolle Alternative herauskristallisiert, die einen flexibleren und effizienteren Ansatz zum Abrufen von Daten bietet. Für Entwickler, die mit Java vertraut sind, eröffnet die Integration von GraphQL in ein modernes Backend die Möglichkeit, skalierbare und leistungsstarke APIs zu erstellen, die auf eine Vielzahl von Anwendungsfällen zugeschnitten sind.
Dieser Blog wird die wichtigsten Unterschiede zwischen GraphQL und REST untersuchen, die einzigartigen Vorteile der Verwendung von GraphQL zum Abrufen von Daten hervorheben und Sie durch die Implementierung einer GraphQL-API in Java mit einem realen Beispiel führen.
Was ist GraphQL?
GraphQL ist eine Abfragesprache für APIs und eine Laufzeitumgebung zum Ausführen dieser Abfragen. Im Gegensatz zu REST, bei dem feste Endpunkte vordefinierte Daten zurückgeben, ermöglicht GraphQL den Clients, genau die Daten anzufordern, die sie benötigen. Diese Granularität macht GraphQL besonders effizient, insbesondere für komplexe oder datengesteuerte Anwendungen.
Vorteile des GraphQL-Ansatzes:
- Granulares Datenabrufen: Der Client kann nur Name und Bezeichnung abfragen, ohne unnötige Felder wie Abteilung abzurufen.
- Verschachtelte Abfragen: Abrufen der Managerdetails zusammen mit den Mitarbeiterinformationen in einer einzigen Abfrage.
- Schema-gesteuerte Entwicklung: Das Schema fungiert als Vertrag, der die Evolution der API erleichtert.
Was ist eine REST-API?
Representational State Transfer (REST) ist ein Architekturstil für den Aufbau von APIs. Es verwendet standardisierte HTTP-Methoden wie GET, POST, PUT und DELETE, um CRUD-Operationen durchzuführen. REST ist bekannt für seine Einfachheit und weit verbreitete Anwendung.
Einschränkungen von REST:
- Über- oder Unterabfrage von Daten.
- Erfordert mehrere Endpunkte und Versionierung, um Änderungen anzupassen.
- Keine integrierten Echtzeitfunktionen.
GraphQL vs. REST-API: Was ist der Unterschied?
GraphQL und REST sind zwei beliebte Ansätze zum Erstellen von APIs, von denen jeder seine Stärken hat. Während REST jahrelang der Standard war, bietet GraphQL mehr Flexibilität und Effizienz, insbesondere bei der Datenabfrage und der Zusammenarbeit zwischen Frontend- und Backend-Teams.
Wesentliche Unterschiede
- Im Gegensatz zu REST, das mehrere Endpunkte verwendet und für Änderungen eine Versionierung erfordert, konsolidiert GraphQL die Datenabfrage in eine einzige Abfrage und reduziert den Bedarf an Versionierung, da Clients die Datenanforderungen angeben.
- Während REST HTTP-Statuscodes verwendet, um Erfolg oder Fehler anzuzeigen, gibt GraphQL immer einen 200 OK-Status zurück und kommuniziert Fehler im Antworttext.
- GraphQL unterstützt auch Echtzeitaktualisierungen über Abonnements, im Gegensatz zu REST, das keine integrierte Echtzeitunterstützung bietet.
- Obwohl REST weit verbreitet ist und viele Werkzeuge bietet, hat sich die Umgebung von GraphQL schnell entwickelt und bietet leistungsstarke Tools wie GraphiQL für eine einfachere Entwicklung.
- Zuletzt, während REST Header für das Caching verwendet, erfordert GraphQL aufgrund dynamischer Abfragen fortgeschrittenere Techniken, bietet jedoch Optionen wie persistente Abfragen für effizientes Caching.
Kernkonzepte von GraphQL
1. Schema-Definition-Sprache (SDL)
GraphQL hat sein eigenes Typsystem, das verwendet wird, um das Schema einer API zu definieren. Die Syntax zum Schreiben von Schemata wird Schema-Definition-Sprache (SDL) genannt.
2. Abfragen vs. Mutationen vs. Abonnements
- Abfragen werden verwendet, um Daten vom Server abzurufen. Im Gegensatz zu REST, das mehrere feste Endpunkte verwendet, nutzt GraphQL einen einzigen Endpunkt, und der Client gibt die benötigten Daten in der Abfrage an, was Flexibilität bietet.
- Mutationen werden verwendet, um Daten auf dem Server zu ändern, wie das Erstellen, Aktualisieren oder Löschen von Daten. Sie ermöglichen es den Clients, Änderungen an das Backend zu senden und sind unerlässlich für Anwendungen, die Daten schreiben müssen.
- Abonnements ermöglichen Echtzeit-Updates, indem sie eine ständige Verbindung zwischen dem Client und dem Server aufrechterhalten. Wenn ein abonnierter Ereignis eintritt, sendet der Server Updates an den Client und bietet kontinuierliche Datenströme, im Gegensatz zu Abfragen und Mutationen, die einem Anfrage-Antwort-Zyklus folgen.
3. GraphQL-Schema
Es definiert die Datenstruktur, die abgefragt oder verändert werden kann, und fungiert als Vertrag zwischen Server und Client. Es spezifiziert die Typen, Felder und Beziehungen, auf die Clients zugreifen können. Das Schema umfasst in der Regel spezielle Wurzeltypen: Abfrage für die Datenabfrage, Mutation zur Änderung von Daten und Abonnement für Echtzeitaktualisierungen. Diese Typen definieren zusammen die Fähigkeiten der API und wie Clients mit ihr interagieren können.
4. Resolver: Zuordnung von GraphQL-Abfragen zu Daten
Resolver sind Funktionen, die die Logik für das Abrufen von Daten in einem GraphQL-Server behandeln. Jedes Feld in einem Schema ist mit einem Resolver verknüpft, der bestimmt, wie die Daten für dieses Feld abgerufen oder berechnet werden. Wenn eine Abfrage ausgeführt wird, ruft der Server die entsprechenden Resolver für die angeforderten Felder auf. Resolver können Skalare oder Objekte zurückgeben, wobei die Ausführung für untergeordnete Felder fortgesetzt wird, wenn ein Objekt zurückgegeben wird, und abgeschlossen wird, wenn ein Skalar zurückgegeben wird. Wenn null zurückgegeben wird, stoppt die Ausführung. Resolver sind unerlässlich für die Zuordnung von GraphQL-Abfragen zu den tatsächlichen Datenquellen.
Vorteile der Verwendung von GraphQL in Java
- Exakte Datenabfrage: Abfragen Sie nur die benötigten Daten, nichts mehr, um vorhersehbare und effiziente Ergebnisse zu gewährleisten.
- Eine Anfrage für mehrere Ressourcen: Rufen Sie verwandte Daten in einer Abfrage ab, um mehrere API-Aufrufe zu reduzieren.
- Typsystem: Organisiert APIs nach Typen und Feldern, um sicherzustellen, dass Abfragen gültig sind und Fehler klar sind.
- Entwicklerwerkzeuge: Steigern Sie die Produktivität mit Tools wie GraphiQL, verwenden Sie Typdefinitionen für besseren Abfrageaufbau und Debugging.
- Versionlose Evolution: Fügen Sie Felder hinzu oder veralten Sie sie, ohne bestehende Abfragen zu unterbrechen, um die APIs wartbar zu halten.
- Flexible Datenintegration: Erstellen Sie eine einheitliche API über vorhandenen Daten und Code, die mit verschiedenen Speicher-Engines und Sprachen kompatibel ist.
Einrichten einer GraphQL-API in Java
Praxisbeispiel: Benutzer und Aufträge
Stellen Sie sich vor, Sie erstellen eine Mitarbeiterverzeichnis-API für eine große Organisation. Das Ziel ist es, es den Clients zu ermöglichen, Details wie den Namen des Mitarbeiters, die Bezeichnung, die Abteilung und sogar deren Berichtslinie abzufragen.
1. Projekt einrichten
Erstellen Sie ein neues Spring Boot-Projekt mit Spring Tool Suite oder besuchen Sie Spring Initialiser. Fügen Sie dann diese Abhängigkeiten zur pom.xml
-Datei hinzu:
<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. Erstellen Sie Ihre Entitäten
Erstellen Sie Java-Entitäten (z. B. Benutzer
und Auftrag
), um die Daten darzustellen, die über GraphQL abgefragt oder mutiert werden. Zum Beispiel:
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. Repositories erstellen
Erstellen Sie Repositories, um mit der Datenbank zu interagieren:
public interface UserRepository extends JpaRepository<User, Long> {}
public interface OrderRepository extends JpaRepository<Order, Long> {}
4. Service-Klassen erstellen
Erstellen Sie Service-Klassen, um die Geschäftslogik zu verarbeiten:
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. Erstellen Sie GraphQL-Controller
Definieren Sie GraphQL-Controller, um Abfragen und Mutationen zu verarbeiten:
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. Definieren Sie Ihr GraphQL-Schema
Erstellen Sie eine schema.graphqls
-Datei im Verzeichnis 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. Konfigurieren Sie GraphQL in der application.properties
Optional können Sie GraphQL-Einstellungen in scr/main/resources/application.properties
konfigurieren:
spring.graphql.graphiql.enabled=true
8. Führen Sie Ihre Anwendung aus
Führen Sie die SpringBoot-Anwendung mit mvn spring-boot:run
oder aus Ihrer IDE aus. Sobald sie läuft, können Sie auf den GraphAL-Endpunkt unter /graphiql
zugreifen.
9. Testen Sie mit GraphQL-Abfragen
Testen Sie die GraphQL-API mit einem Tool wie GraphiQL oder Postman.
Für Mutation:
mutation {
createUser(
name:"swetha",
email:"[email protected]",
password:"23sde4dfg43"
){
name,
userId
}
}
Ausgabe:
{
"data": {
"createUser": {
"name": "swetha",
"userId": "3"
}
}
}
Für Abfrage:
query{
getUsers{
name
}
}
Ausgabe:
{
"data": {
"getUsers": [
{
"name": "Medha"
},
{
"name": "Riya"
},
{
"name": "swetha"
}
]
}
}
Erweiterte GraphQL-Funktionen
1. Verbessern der Wiederverwendbarkeit mit Fragments
Ein Fragment ist im Grunde eine wiederverwendbare Gruppe von Feldern, die für einen bestimmten Typ definiert sind. Es ist eine Funktion, die die Struktur und Wiederverwendbarkeit Ihres GraphQL-Codes verbessert.
2. Feldparameterisierung mit Argumenten
In GraphQL können Felder Argumente akzeptieren, um Abfragen dynamischer und flexibler zu gestalten. Diese Argumente ermöglichen es, die von der API zurückgegebenen Daten zu filtern oder anzupassen.
3. Paginierung und Sortierung mit GraphQL
Paginierung
Paginierung ist ein schwieriges Thema im API-Design. Auf einer hohen Ebene gibt es zwei Hauptansätze, wie sie angegangen werden kann.
- Limit-Offset: Fordern Sie einen bestimmten Abschnitt der Liste an, indem Sie die Indizes der abzurufenden Elemente angeben (tatsächlich geben Sie hauptsächlich den Startindex (Offset) sowie eine Anzahl von abzurufenden Elementen (Limit) an).
- Cursor-basiert: Dieses Paginierungsmodell ist etwas fortgeschrittener. Jedes Element in der Liste ist mit einer eindeutigen ID (dem Cursor) verknüpft. Clients, die durch die Liste paginieren, geben dann den Cursor des Start-Elements sowie eine Anzahl von abzurufenden Elementen an.
Sortierung
Mit GraphQL API Design ist es möglich, Listen von Elementen zurückzugeben, die entsprechend spezifischer Kriterien sortiert (geordnet) sind.
Herausforderungen und Überlegungen zur Verwendung von GraphQL
- Komplexität: Die Verwaltung von GraphQL-Schemas und -Abfragen kann für einfache Datenmodelle oder unerfahrene Teams herausfordernd sein.
- Leistungsprobleme: Tief verschachtelte Abfragen können die Backend-Ressourcen belasten, wenn sie nicht optimiert sind.
- Caching-Herausforderungen: Standard-REST-basierte Caching-Strategien sind nicht anwendbar und erfordern individuelle Lösungen.
- Sicherheitsbedenken: Übermäßiges Abrufen und bösartige Abfragen erfordern Abfragegrenzen und andere Sicherheitsvorkehrungen.
- Hybrid-Nutzung: Funktioniert am besten für komplexe Datenanforderungen, oft in Kombination mit REST für einfachere Operationen.
Fazit
GraphQL bietet einen flexiblen und effizienten Ansatz zum Erstellen moderner APIs in Java und ist daher eine ideale Wahl für dynamische und datenintensive Anwendungen. Die Architektur mit nur einem Endpunkt und die starke Typisierung vereinfachen das Design von APIs und gewährleisten dabei eine robuste Leistung. Egal, ob Sie ein einfaches Mitarbeiterverzeichnis oder eine komplexe Analyseplattform erstellen, GraphQL ermöglicht es Entwicklern, skalierbare Lösungen mit Leichtigkeit bereitzustellen. Beginnen Sie noch heute mit der Erkundung von GraphQL mit Tools wie Spring Boot und graphql-java
, um das volle Potenzial in Ihrem nächsten Projekt auszuschöpfen.
Quellcode
Sie finden den vollständigen Quellcode für dieses Tutorial auf Github.
Source:
https://dzone.com/articles/design-scalable-java-apis-with-graphql