REST-APIs entwickeln
Mit LowCode erstellen Sie in wenigen Minuten vollständige REST-APIs. Die http in- und http response-Nodes von Node-RED bilden die Grundlage, während Function-Nodes die Geschäftslogik übernehmen.
Grundprinzip
Jeder HTTP-Endpunkt besteht aus mindestens zwei Nodes:
[http in: GET /api/resource] → [Verarbeitung] → [http response]Der http in-Node empfängt die Anfrage, beliebige Zwischenknoten verarbeiten die Daten, und der http response-Node sendet die Antwort zurück.
HTTP-Methoden
Node-RED unterstützt alle gängigen HTTP-Methoden:
| Methode | Verwendung | Beispiel-Pfad |
|---|---|---|
GET | Daten abrufen | /api/customers |
POST | Neue Ressource erstellen | /api/customers |
PUT | Ressource vollständig aktualisieren | /api/customers/:id |
PATCH | Ressource teilweise aktualisieren | /api/customers/:id |
DELETE | Ressource löschen | /api/customers/:id |
Request- und Response-Handling
Request-Daten auslesen
Im Function-Node stehen Ihnen alle Request-Daten zur Verfügung:
// URL-Parameter (z.B. /api/customers/:id)
const customerId = msg.req.params.id;
// Query-Parameter (z.B. /api/customers?status=active)
const status = msg.req.query.status;
// Request-Body (bei POST/PUT/PATCH)
const body = msg.payload;
// HTTP-Header
const authHeader = msg.req.headers["authorization"];Response konfigurieren
Steuern Sie die Antwort über die msg-Eigenschaften:
// Statuscode setzen
msg.statusCode = 200;
// Response-Header setzen
msg.headers = {
"Content-Type": "application/json",
"X-Request-Id": msg.req.headers["x-request-id"] || "none"
};
// Response-Body setzen
msg.payload = {
success: true,
data: { id: 1, name: "Max Mustermann" }
};
return msg;Vollständiges CRUD-Beispiel
Das folgende Beispiel zeigt eine vollständige CRUD-API für eine Ressource products. Die Daten werden im Flow-Context gespeichert (in der Praxis verwenden Sie eine Datenbank).
Flow-Aufbau
[http in: GET /api/products] → [fn: listProducts] → [http response]
[http in: GET /api/products/:id] → [fn: getProduct] → [http response]
[http in: POST /api/products] → [fn: createProduct] → [http response]
[http in: PUT /api/products/:id] → [fn: updateProduct] → [http response]
[http in: DELETE /api/products/:id] → [fn: deleteProduct] → [http response]GET — Alle Produkte abrufen
// Function Node: listProducts
const products = flow.get("products") || [];
// Optionale Filterung via Query-Parameter
const category = msg.req.query.category;
const filtered = category
? products.filter(p => p.category === category)
: products;
msg.payload = {
success: true,
count: filtered.length,
data: filtered
};
msg.statusCode = 200;
return msg;GET — Einzelnes Produkt abrufen
// Function Node: getProduct
const products = flow.get("products") || [];
const product = products.find(p => p.id === msg.req.params.id);
if (!product) {
msg.statusCode = 404;
msg.payload = { success: false, error: "Produkt nicht gefunden" };
return msg;
}
msg.statusCode = 200;
msg.payload = { success: true, data: product };
return msg;POST — Neues Produkt erstellen
// Function Node: createProduct
const products = flow.get("products") || [];
const body = msg.payload;
// Validierung
if (!body.name || !body.price) {
msg.statusCode = 400;
msg.payload = {
success: false,
error: "Felder 'name' und 'price' sind erforderlich"
};
return msg;
}
// Neues Produkt anlegen
const newProduct = {
id: `prod-${Date.now()}`,
name: body.name,
price: body.price,
category: body.category || "Allgemein",
createdAt: new Date().toISOString()
};
products.push(newProduct);
flow.set("products", products);
msg.statusCode = 201;
msg.payload = { success: true, data: newProduct };
return msg;PUT — Produkt aktualisieren
// Function Node: updateProduct
const products = flow.get("products") || [];
const index = products.findIndex(p => p.id === msg.req.params.id);
if (index === -1) {
msg.statusCode = 404;
msg.payload = { success: false, error: "Produkt nicht gefunden" };
return msg;
}
const body = msg.payload;
products[index] = {
...products[index],
name: body.name || products[index].name,
price: body.price || products[index].price,
category: body.category || products[index].category,
updatedAt: new Date().toISOString()
};
flow.set("products", products);
msg.statusCode = 200;
msg.payload = { success: true, data: products[index] };
return msg;DELETE — Produkt löschen
// Function Node: deleteProduct
const products = flow.get("products") || [];
const index = products.findIndex(p => p.id === msg.req.params.id);
if (index === -1) {
msg.statusCode = 404;
msg.payload = { success: false, error: "Produkt nicht gefunden" };
return msg;
}
const deleted = products.splice(index, 1)[0];
flow.set("products", products);
msg.statusCode = 200;
msg.payload = { success: true, data: deleted };
return msg;Authentication mit JWT
Sichern Sie Ihre API-Endpunkte mit JWT-Tokens ab. Die Validierung erfolgt über einen zwischengeschalteten Function-Node.
Flow mit Authentication
[http in: GET /api/products]
→ [fn: validateJwt]
→ [switch: valid?]
├─ Ja → [fn: listProducts] → [http response]
└─ Nein → [http response: 401]JWT-Validierung
// Function Node: validateJwt
const authHeader = msg.req.headers["authorization"];
if (!authHeader || !authHeader.startsWith("Bearer ")) {
msg.statusCode = 401;
msg.payload = { success: false, error: "Kein Token vorhanden" };
return [null, msg]; // Zweiter Ausgang: Fehler
}
const token = authHeader.split(" ")[1];
try {
// Token über Authority validieren
msg.url = `${env.get("AUTHORITY_URL")}/connect/userinfo`;
msg.headers = { "Authorization": `Bearer ${token}` };
msg.method = "GET";
return [msg, null]; // Erster Ausgang: Token-Validierung via HTTP Request
} catch (error) {
msg.statusCode = 401;
msg.payload = { success: false, error: "Token ungültig" };
return [null, msg];
}Error Handling
Implementieren Sie eine einheitliche Fehlerbehandlung mit dem catch-Node:
Globaler Error Handler
[catch: Alle Nodes]
→ [fn: formatError]
→ [http response]// Function Node: formatError
const error = msg.error;
msg.statusCode = msg.statusCode || 500;
msg.payload = {
success: false,
error: error.message || "Interner Serverfehler",
timestamp: new Date().toISOString()
};
// Fehler im Debug loggen
node.error(`API-Fehler: ${error.message}`, msg);
return msg;OpenAPI / Swagger-Integration
LowCode Enterprise bietet einen integrierten OpenAPI-Generator, der automatisch eine Swagger-Dokumentation aus Ihren HTTP-Endpunkten erstellt.
Automatische Dokumentation
Wenn Sie das Enterprise Image verwenden, steht Ihnen unter dem Pfad /api-docs eine automatisch generierte OpenAPI-Spezifikation zur Verfügung. Diese basiert auf den konfigurierten http in-Nodes.
Manuelle OpenAPI-Spezifikation
Alternativ können Sie eine OpenAPI-Spezifikation als statischen Endpunkt bereitstellen:
// Function Node: serveOpenApiSpec
msg.headers = { "Content-Type": "application/json" };
msg.payload = {
openapi: "3.0.0",
info: {
title: "Products API",
version: "1.0.0",
description: "Produkt-Verwaltungs-API via ProcessCube® LowCode"
},
paths: {
"/api/products": {
get: {
summary: "Alle Produkte abrufen",
parameters: [
{ name: "category", in: "query", schema: { type: "string" } }
],
responses: {
200: { description: "Erfolgreiche Antwort" }
}
}
}
}
};
return msg;Best Practices
1. Konsistente URL-Struktur
Verwenden Sie eine einheitliche Namenskonvention:
/api/v1/resources # Plural, Kleinbuchstaben
/api/v1/resources/:id # Einzelne Ressource
/api/v1/resources/:id/sub # Unterressourcen2. Versionierung
Fügen Sie eine Versionierung in den URL-Pfad ein:
/api/v1/products # Version 1
/api/v2/products # Version 23. Standardisierte Responses
Halten Sie die Response-Struktur konsistent:
// Erfolg
{ success: true, data: {...}, count: 10 }
// Fehler
{ success: false, error: "Beschreibung", code: "VALIDATION_ERROR" }4. Rate Limiting
Schützen Sie Ihre API vor Überlastung mit einem einfachen Rate Limiter:
// Function Node: rateLimiter
const ip = msg.req.ip;
const requests = flow.get("rateLimit") || {};
const now = Date.now();
// Einträge älter als 1 Minute entfernen
requests[ip] = (requests[ip] || []).filter(t => now - t < 60000);
if (requests[ip].length >= 100) {
msg.statusCode = 429;
msg.payload = { success: false, error: "Zu viele Anfragen" };
flow.set("rateLimit", requests);
return [null, msg]; // Zweiter Ausgang: Rate Limit
}
requests[ip].push(now);
flow.set("rateLimit", requests);
return [msg, null]; // Erster Ausgang: WeiterNächste Schritte
- Integrationen bauen — APIs mit Drittsystemen verbinden
- Authentication-Features — Erweiterte Authentication-Optionen
- Node-RED Grundlagen — Grundkonzepte vertiefen