Eine API (Application Programming Interface) ist die Schnittstelle, über die verschiedene Software-Systeme miteinander kommunizieren.
API-Arten vs. HTTP-Methoden
Ein häufiges Missverständnis ist die Verwechslung von API-Arten (wie REST oder GraphQL) und HTTP-Methoden (wie GET oder POST):
- API-Arten (Architekturstile/Protokolle): Bestimmen die Regeln, das Format und die Struktur, wie Nachrichten zwischen Client und Server ausgetauscht werden (z. B. REST, GraphQL, gRPC).
- HTTP-Methoden: Sind die Aktionen/Verben, die innerhalb von webbasierten APIs (insbesondere REST) genutzt werden, um dem Server mitzuteilen, was mit einer Ressource getan werden soll.
Die HTTP-Methoden (Verben der API-Welt)
In HTTP-basierten APIs (wie REST) steuern diese Methoden die Operationen auf Daten Ressourcen:
| Methode | CRUD-Äquivalent | Beschreibung | Idempotent? | Sicher? |
|---|---|---|---|---|
GET | Read | Ruft Daten ab. Verändert nichts auf dem Server. | Ja | Ja |
POST | Create | Erstellt eine neue Ressource auf dem Server. | Nein | Nein |
PUT | Update / Replace | Ersetzt eine komplette Ressource oder erstellt sie, falls nicht vorhanden. | Ja | Nein |
PATCH | Partial Update | Aktualisiert nur einzelne Felder einer Ressource. | Nein | Nein |
DELETE | Delete | Löscht eine bestimmte Ressource. | Ja | Nein |
Hinweise: Idempotent: Mehrfache identische Aufrufe haben dasselbe Ergebnis auf dem Server wie ein einzelner Aufruf. Sicher: Der Aufruf verändert den Zustand des Servers nicht (nur Lesezugriff).
Die wichtigsten API-Arten & Architektur-Stile
REST (Representational State Transfer)
REST ist der heute am weitesten verbreitete Standard für Web-APIs. Es nutzt Standard-HTTP-Methoden und überträgt Daten meist im JSON-Format.
- Zweck: Standardisierte, stateless Kommunikation über Web-Ressourcen.
- Vorteile:
- Einfach zu verstehen und weit verbreitet.
- Hohe Skalierbarkeit durch Caching (HTTP-Level).
- Entkopplung von Client und Server.
- Nachteile:
- Over-Fetching: Man bekommt oft mehr Daten als benötigt.
- Under-Fetching: Man muss oft mehrere Endpunkte aufrufen (z. B.
/users/1und dann/users/1/posts).
Minimalbeispiel (REST)
Frontend (JavaScript - Browser):
// POST-Request zum Erstellen eines Benutzers
async function createUser() {
const response = await fetch('https://api.example.com/users', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ name: 'Max Mustermann', email: 'max@example.com' })
});
const data = await response.json();
console.log('Erstellter User:', data);
}Backend (Node.js mit Express):
const express = require('express');
const app = express();
app.use(express.json());
// REST Endpunkt für POST /users
app.post('/users', (req, res) => {
const newUser = { id: Date.now(), ...req.body };
res.status(201).json(newUser);
});
app.listen(3000, () => console.log('REST-Server läuft auf Port 3000'));GraphQL
GraphQL ist eine von Meta entwickelte Abfragesprache für APIs. Der Client bestimmt exakt, welche Daten er zurückerhalten möchte.
- Zweck: Präzise Datenabfrage mit nur einem einzigen Endpunkt.
- Vorteile:
- Kein Over- oder Under-Fetching (Client fordert exakt benötigte Felder an).
- Ein einziger Endpunkt (
/graphql) für alle Anfragen. - Starke Typisierung durch Schemas.
- Nachteile:
- Komplexere Serverseitige Caching-Mechanismen im Vergleich zu REST.
- Steilere Lernkurve beim Aufsetzen.
Minimalbeispiel (GraphQL)
Frontend (JavaScript):
// GraphQL Anfrage sendet immer einen POST-Request mit einer 'query'
async function fetchUser() {
const query = `
query {
user(id: "1") {
name
}
}
`;
const response = await fetch('http://localhost:4000/graphql', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ query })
});
const result = await response.json();
console.log(result.data.user); // Nur { name: "Max" } wird zurückgegeben
}Backend (Node.js mit Apollo Server):
const { ApolloServer, gql } = require('apollo-server');
const typeDefs = gql`
type User { id: ID!, name: String! }
type Query { user(id: ID!): User }
`;
const resolvers = {
Query: {
user: (_, { id }) => ({ id, name: 'Max Mustermann' })
}
};
const server = new ApolloServer({ typeDefs, resolvers });
server.listen(4000).then(({ url }) => console.log(`GraphQL Server auf ${url}`));WebSockets
WebSockets bieten eine dauerhafte, bidirektionale Full-Duplex-Verbindung zwischen Client und Server.
- Zweck: Echtzeit-Kommunikation (z. B. Chat-Apps, Live-Ticker, Finanzdaten).
- Vorteile:
- Sehr geringe Latenz.
- Server kann eigenständig Daten an den Client “pushen” (ohne dass der Client fragen muss).
- Nachteile:
- Schwerer zu skalieren und zu meistern (Verbindungsaufbau muss gehalten werden).
- Kein integriertes Caching.
Minimalbeispiel (WebSockets)
Frontend (JavaScript):
const socket = new WebSocket('ws://localhost:8080');
// Nachricht empfangen
socket.onmessage = (event) => {
console.log('Nachricht vom Server:', event.data);
};
// Nachricht senden
function sendMessage() {
socket.send('Hallo Server!');
}Backend (Node.js mit ws Modul):
const WebSocket = require('ws');
const wss = new WebSocket.Server({ port: 8080 });
wss.on('connection', (ws) => {
console.log('Client verbunden');
ws.on('message', (message) => {
console.log('Empfangen:', message.toString());
ws.send('Antwort: Nachricht erhalten!');
});
});gRPC (Google Remote Procedure Call)
Ein modernes, extrem schnelles Framework von Google, das HTTP/2 und Protocol Buffers (protobuf) nutzt.
- Zweck: Hochleistungs-Mikroservice-Kommunikation.
- Vorteile:
- Extrem schnell und performant (binäre Datenübertragung statt Text/JSON).
- Automatische Code-Generierung für Client und Server in vielen Sprachen.
- Unterstützt Streaming (Client, Server, Bidirektional).
- Nachteile:
- Für Menschen nicht direkt lesbare Daten (Binärformat).
- Eingeschränkter Browser-Support (oft
gRPC-WebProxy nötig).
Minimalbeispiel (gRPC Schema & Backend-Logik)
Proto-Datei (user.proto):
syntax = "proto3";
service UserService {
rpc GetUser (UserRequest) returns (UserResponse);
}
message UserRequest { string id = 1; }
message UserResponse { string id = 1; string name = 2; }Backend (Node.js gRPC Server Snippet):
const grpc = require('@grpc/grpc-js');
const protoLoader = require('@grpc/proto-loader');
const packageDef = protoLoader.loadSync('user.proto');
const proto = grpc.loadPackageDefinition(packageDef);
const server = new grpc.Server();
server.addService(proto.UserService.service, {
getUser: (call, callback) => {
callback(null, { id: call.request.id, name: 'Max' });
}
});
server.bindAsync('0.0.0.0:50051', grpc.ServerCredentials.createInsecure(), () => server.start());SOAP (Simple Object Access Protocol)
Ein älteres, streng reglementiertes Protokoll, das ausschließlich XML zur Datenübertragung nutzt.
- Zweck: Hochsichere Transaktionen in Enterprise-Umgebungen (Banken, Versicherungen, Legacy-Systeme).
- Vorteile:
- Eingebaute Sicherheits- und Transaktionsstandards (WS-Security, ACID-Transaktionen).
- Strikte Vertragsbindung durch WSDL (Web Services Description Language).
- Nachteile:
- Sehr komplex und “schwerfällig” (Große XML-Payloads).
- Aufwendig zu implementieren.
Minimalbeispiel (SOAP Request Payload)
Request (XML via POST):
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:usr="http://example.com/user">
<soapenv:Header/>
<soapenv:Body>
<usr:GetUserRequest>
<usr:id>1</usr:id>
</usr:GetUserRequest>
</soapenv:Body>
</soapenv:Envelope>Webhooks (Event-driven API)
Webhooks kehren das klassische Client-Server-Prinzip um: Der Server ruft eine URL des Clients auf, sobald ein bestimmtes Ereignis eintritt (“Push” statt “Pull”).
- Zweck: Benachrichtigungen über Events in Echtzeit (z. B. Stripe Zahlung erfolgreich, GitHub Push Event).
- Vorteile:
- Kein ständiges Abfragen (“Polling”) vom Client nötig -> spart Ressourcen.
- Nachteile:
- Der Empfänger muss einen öffentlich erreichbaren Endpunkt bereitstellen.
- Re-Try Logik muss bei Ausfällen vom Sender verwaltet werden.
Minimalbeispiel (Webhook Receiver)
1. Sender / Auslöser (z. B. Zahlungsdienstleister oder eigener Event-Service):
// Backend des Senders: Sendet einen HTTP POST Request bei Eintritt eines Events
async function sendWebhookNotification(targetUrl, payload) {
try {
const response = await fetch(targetUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Webhook-Signature': 'secret_hash_signature' // Sicherheits-Token/Signatur
},
body: JSON.stringify(payload)
});
if (response.ok) {
console.log('Webhook erfolgreich an Empfänger zugestellt!');
}
} catch (error) {
console.error('Fehler beim Zustellen des Webhooks:', error);
}
}
// Beispiel: Wird aufgerufen, wenn ein Kauf abgeschlossen wurde
sendWebhookNotification('https://kunden-backend.de/api/webhook/payment', {
event: 'payment.succeeded',
orderId: 'ORDER_12345',
amount: 49.99,
currency: 'EUR'
});2. Empfänger / Receiver (z. B. Dein Express-Backend):
const express = require('express');
const app = express();
app.use(express.json());
// Webhook-Endpunkt, den der externe Dienst aufruft
app.post('/api/webhook/payment', (req, res) => {
const event = req.body;
console.log('Event empfangen:', event.event, 'für Bestellung:', event.orderId);
// Verarbeite die Logik (z. B. Paket versenden, DB aktualisieren)
// WICHTIG: Dem Sender schnell mit HTTP 200 (OK) quittieren!
res.status(200).send('Webhook erhalten');
});
app.listen(3000);