Funktionen & HTTP-Services mit ScriptEngine
Track E dockt AuxData an die Außenwelt an. In E4 baust du die ScriptEngine-Mechanik selbst: das Modell, die Sprache ES5.1, die Module mit ihren Signaturen, Umgebungsvariablen, den HTTP-Service-Editor und eine vollständige, robuste Funktion.
Was du in diesem Tutorial baust
Wir bauen die Preisabfrage-/E-Mail-Funktion aus C5 als eigenständiges, robustes Modul nach — eine ScriptEngine-Funktion, die einen Parameter validiert, per HTTP einen externen Dienst abfragt, das Ergebnis protokolliert und über einen sauberen Fehlerpfad immer einen definierten String zurückliefert. Dazu kommt der passende HTTP-Service im Editor.
Voraussetzung: Track B, gut ergänzt durch C5 (HTTP/Funktion/MCP im Workflow), und ein Grundverständnis von JavaScript. Keine echten Secrets, Tokens oder Endpunkte — alle Zugangsdaten liegen in Umgebungsvariablen (api_token, db_password) bzw. als ${env.NAME}-Platzhalter.
Quellen und Stand
Geprüft gegen das Programmierhandbuch der ScriptEngine (Einführung, ECMAScript-5.1-Referenz, Umgebungsvariablen, Module, Code-Beispiele, Best Practices, Tutorial „Erste Funktion erstellen") sowie das Administrator-Handbuch Kapitel 7.1 (HTTP-Services) und 7.2 (Funktionen).
ScriptEngine-Modell
Wie eine Funktion läuft — und warum jeder Parameter ein String ist.
1Otto-Engine und ES5.1-Sandbox
Die ScriptEngine basiert auf der Otto-JavaScript-Engine und unterstützt ECMAScript 5.1. Sie läuft als Sandbox ohne Dateisystem- oder Netz-IO außerhalb der eingebauten Module. (PH Einführung; AH 7.2)
Du schreibst keine vollständige Datei, sondern nur den Rumpf einer Funktion. Die Plattform legt einen künstlichen Funktionskopf um deinen Code und ruft ihn mit den übergebenen Parametern auf.
In Klartext: Dein Code läuft in einer abgeschotteten Mini-JavaScript-Umgebung — kein Internet- oder Dateizugriff außer über die freigegebenen Module. Du schreibst nur den Inhalt der Funktion; den Rahmen drumherum setzt AuxData automatisch.
2Funktionssignatur & String-Konvertierung
Die Plattform umschließt deinen Code etwa so — du lieferst nur den markierten Rumpf:
// Künstlicher Kopf der Plattform (nur zur Veranschaulichung):
function executorFunctionWrapper(parameter1, parameter2) {
// ── ab hier schreibst du den Rumpf ──
// Wichtig: ALLE Parameter kommen als String an!
var summe = parseInt(parameter1) + parseInt(parameter2); // numerisch
return String(summe); // Rückgabe wird als String erwartet
}
return parameter1 + parameter2 die Zeichenkette "58" statt 13 — weil Strings verkettet werden. Erst parseInt(...) bzw. parseFloat(...) rechnet numerisch.Der Rückgabewert wird als String erwartet. Ohne return oder bei einem Abbruch im Fehlerfall ist der Rückgabewert undefined.
return zahl1 + zahl2 bei den Eingaben 5 und 8 das Ergebnis „58" — und wie behebst du es?✓ Das hast du jetzt verstanden
Sprache & Muster (ECMAScript 5.1)
Was Otto kann — und welche ES6-Annahmen scheitern.
1Was ES5.1 unterstützt
Unterstützt sind var (kein let/const), die Typen String/Number/Boolean/Array/Object/null/undefined, die üblichen Operatoren, if/else, for, while, JSON.stringify/JSON.parse sowie eingebaute String-/Array-/Math-/Date-Funktionen. (PH ECMAScript-5.1-Referenz)
var namen = ["Anna", "Ben"]; // Array mit []
namen.push("Carla"); // mit push füllen
var antwort = "";
for (var i = 0; i < namen.length; i++) {
if (namen[i] === "Ben") { // strenge Gleichheit
antwort = "gefunden";
}
}
var daten = JSON.parse('{"ok": true}'); // String -> Objekt
var text = JSON.stringify(daten); // Objekt -> String
2Nicht unterstützt (ES6+)
Diese Konstrukte laufen in Otto nicht: let/const, Arrow-Funktionen, Template-Strings, Destructuring, Spread, class, Promise, async/await.
`Hallo ${name}`) und Arrow-Funktionen (var f = () => x) scheitern. Nutze "Hallo " + name und klassische function-Ausdrücke.Häufige Fehler: var nicht vergessen (sonst entsteht eine globale Variable); konsequent strenge Gleichheit (===) verwenden; Arrays als [] anlegen und mit push füllen.
let x = 1, var x = 1, var f = () => x, function f() {}?✓ Das hast du jetzt verstanden
Module & Signaturen
Erst getModule, dann die Funktionen — mit exakten Signaturen.
1Module initialisieren
Jedes Modul wird zuerst mit getModule("modulname") initialisiert; danach stehen seine Funktionen als globale Namen bereit. (PH Module; AH 7.2)
getModule("log");
getModule("http");
log_info("Modul http bereit");
var antwort = http_get("https://api.example.com/v1/data", {});
🧠 Für Profis: Schrittergebnisse ohne getModule — aiserviceresults
aiserviceresults bereit — anders als Module braucht sie kein getModule(). Die Reihenfolge ist chronologisch: aiserviceresults[0] ist der älteste, aiserviceresults[aiserviceresults.length - 1] der unmittelbar vorhergehende Schritt. Außerhalb eines Workflows (Einzelaufruf) ist sie null — vor dem Zugriff prüfen: if (aiserviceresults === null) { … }. (Programmierhandbuch)2Wichtige Module & exakte Signaturen
- http
http_get(url, header),http_post(url, contentType, header, body),http_put(url, contentType, header, body),http_delete(url, header),http_queryEscape(value),http_search(search, exactMatch).- graphapi
graphapi_getResource(graphApiConfig, url),graphapi_postResource(graphApiConfig, url, body),graphapi_putResource(graphApiConfig, url, body),graphapi_deleteResource(graphApiConfig, url),graphapi_send(graphApiConfig, mail),graphapi_getNewFromInbox(graphApiConfig, maxCount).- aiservice
aiservice_run(agentId, serviceId, params),aiservice_runAsync(agentId, serviceId, params),aiservice_runWithToken(token, agentId, serviceId, params); Ergebnis überresultObj.MultiResults.Results[0].Result.- knowledgedb
saveText(agentId, containerId, name, content, documentId),saveBinary(agentId, containerId, name, content, documentId),find(searchText, agentId, containerId, qualityGate, resultLimit),findByKeyword(searchText, agentId, containerId, resultLimit).- personalknowledgedb
- Lese-/Schreibzugriff auf die persönliche Wissens-DB des ausführenden Users — ohne Agent-/Container-Parameter.
- sql
sql_execute(connectionConfig, sqlCommand),sql_query(connectionConfig, sqlCommand).send(mail, smtpConfig),getNewFromInbox(imapConfig),getByCriteria(imapConfig, criteria),move(mailId, originFolder, destinationFolder, imapConfig).- document
document_readText(agentId, filename, filecontent, readImages).- user
user_getUser().- organisation
organisation_getOrganisation()— liefert u. a.ADConfig(MsGraphTenantId/ClientId/ClientSecret) undTeamsBotConfig; ideal, umgraphApiConfigdirekt aus der Org zu bauen statt aus Umgebungsvariablen.- log
log_info(message),log_warn(message),log_error(message),log_fatal(message),log_logs().- weitere
webcrawler_crawlWeb(url),webcrawler_crawlWebEnhanced(url, depth, pageCount)(max. Tiefe 3, 10 Seiten),webcrawler_liveSearch(prompt, maxPages, searchMode);docdb_load(documentId),docdb_save(documentId, document),docdb_delete(documentId).
sql, graphapi und email brauchen ein Connection- bzw. Config-Objekt als ersten Parameter.MultiResults.Results und filterst versteckte mit results[i].ServiceStepCommand.Displaytype != "Hidden"; aiservice_runWithToken(token, …) erlaubt Aufrufe ohne User-Auth (API-/Integrationskontext). (Programmierhandbuch)✓ Das hast du jetzt verstanden
Umgebungsvariablen & Secrets
Werte aus dem Code heraushalten — und api_token sauber von ${env.NAME} trennen.
1Org- vs. Funktions-Variablen
Umgebungsvariablen halten Werte aus dem Code heraus, die keine Funktionsparameter sind (typisch: API-Tokens). Du setzt sie an der Organisation (gelten für jeden Funktionsaufruf) oder an der Funktion. (PH Umgebungsvariablen; AH 7.1/7.2)
Bei Namensgleichheit überschreibt die Organisation die Funktion. Beim Export einer Funktion wandern die Variablen-Deklarationen, aber nicht die Werte mit — so geraten keine Secrets in eine fremde Organisation. Vordefiniert ist timeout (Sekunden; nur an der Funktion wirksam, an der Organisation ignoriert; Default 600 s).
2Zugriff in der Funktion vs. im HTTP-Service
In der Funktion steht eine Umgebungsvariable als globaler Name bereit:
// Umgebungsvariable "api_token" steht als globaler Name bereit:
var apiKey = api_token;
// niemals hart codieren, niemals ins Log schreiben!
log_info("API-Aufruf vorbereitet"); // ok
// log_info("Token: " + api_token); // FALSCH: Secret im Log
api_token, db_password). Der HTTP-Service-Editor referenziert dieselbe Idee dagegen in der Anfrage über die Template-Syntax ${env.NAME} (AH 7.1).
✓ Das hast du jetzt verstanden
HTTP-Service-Editor
Ein vordefinierter REST-Aufruf — gepflegt auf Organisationsebene.
1Der Editor und seine Reiter
Der HTTP-Service ist ein vordefinierter REST-Aufruf, gepflegt auf Organisationsebene (Org-Admin+). Editor httpserviceeditor.html mit den Reitern Stammdaten, Anfrage, Parameter und Umgebungsvariablen. (AH 7.1)


Parameter (Name, Beschreibung, Default, Pflichtfeld) werden vom Aufrufer bzw. Workflow-Schritt gefüllt und im Body als ${parameter} referenziert. Umgebungsvariablen sind ein separater Schlüssel-Wert-Speicher für Secrets und produktionsabhängige URLs; sie lassen sich über ${env.NAME} in der Anfrage referenzieren und werden verschlüsselt in http_request.env gespeichert. Die eigene Testseite führt einen Probe-Aufruf aus und zeigt Request, Response, Status und Parsing-Ergebnis.
✓ Das hast du jetzt verstanden
Code-Beispiele — eine vollständige Funktion
Validierung → Logging → HTTP-Aufruf → Fehlerpfad.
1Die Demo-Funktion zusammengesetzt
Wir setzen die Demo-Funktion vollständig zusammen, nach dem Muster aus dem PH-Tutorial „Erste Funktion erstellen". (PH Code-Beispiele; AH 7.2)
// Module einmal am Anfang initialisieren
getModule("log");
getModule("http");
// Aufruf protokollieren (keine Rohdaten, keine Secrets)
log_info("Funktion aufgerufen; Parameter name erhalten");
// 1) Parameter validieren
if (!name || typeof name !== "string" || name.trim() === "") {
log_warn("Ungültiger Parameter: name fehlt oder ist leer");
return "FEHLER: Parameter 'name' fehlt oder ist leer.";
}
var cleanName = name.trim();
// 2) Pflicht-Secret prüfen, ohne den Wert zu loggen
if (!api_token || typeof api_token !== "string" || api_token.trim() === "") {
log_error("Umgebungsvariable api_token fehlt");
return "FEHLER: API-Konfiguration fehlt.";
}
var cleanApiToken = api_token.trim();
// 3) HTTP-Aufruf im try-Block
try {
var header = {};
header["Authorization"] = "Bearer " + cleanApiToken; // aus Umgebungsvariable
header["Content-Type"] = "application/json";
// URL-Bestandteile mit dem HTTP-Modul absichern
var sicher = http_queryEscape(cleanName);
var url = "https://api.example.com/v1/greeting?name=" + sicher;
var response = http_get(url, header);
log_info("HTTP-Antwort erhalten");
return "Hallo " + cleanName + "! Antwort: " + response;
} catch (error) {
// 4) Fehlerpfad: immer ein definierter String
log_error("HTTP-Anfrage fehlgeschlagen: " + error);
return "Hallo " + cleanName + "! Willkommen in der ScriptEngine.";
}
Die Funktion gibt über jeden Pfad — Eingabevalidierung, Secret-Prüfung, Erfolg, Fehler — immer einen definierten String zurück. Sie loggt keinen Token-Wert, nutzt den getrimmten Namen nur einmal und escaped URL-Bestandteile mit der vom HTTP-Modul bereitgestellten Funktion.
api_token zurückgeben, damit Nutzende genug Kontext bekommen, aber keinen internen Token-Namen oder Endpunkt sehen?✓ Das hast du jetzt gebaut
Best Practices & Sicherheit
Robust, validiert und sicher — die Regeln aus dem Handbuch.
1Best Practices
Sieben Regeln aus dem Programmierhandbuch, die jede Funktion produktionsreif machen. (PH Best Practices)
- Fehlerbehandlung
- immer
try/catch+log_error. - Logging
- großzügig
log_info, aber keine Passwörter/Tokens loggen. - Module
- nur einmal am Anfang initialisieren.
- Validierung
- Parameter prüfen mit
typeof,isNaN,trim. - Struktur
- Funktionen klein halten und in Teilfunktionen zerlegen.
- Secrets
- sensible Daten in Umgebungsvariablen statt Hartkodierung.
- AI-Ergebnisse
- vor dem Zugriff prüfen:
if (result && result.MultiResults && result.MultiResults.Results.length > 0).
2Sicherheit (AH 7.2)
HTTP-Calls werden gegen die URL-Whitelist geprüft; SQL nur für Admins; die Otto-Sandbox erlaubt kein Dateisystem-/Netz-IO außerhalb der Module. SQL- und HTML-Injection entschärfst du durch Escapen:
// einfache SQL-Escape-Hilfe (Demo): einfache Quotes verdoppeln
var sicher = eingabe.replace(/'/g, "''");
// Logging-Disziplin: Benutzer ja, Passwort niemals
log_info("Login-Versuch für Benutzer: " + benutzername); // erlaubt
// log_info("Passwort: " + passwort); // VERBOTEN
log_info("Login-Versuch für Benutzer: " + benutzername) ist erlaubt — das Passwort darf nicht geloggt werden.Deine Funktion ist robust, validiert und sicher.
Die Preisabfrage-/E-Mail-Funktion steht als eigenständiges Modul: ES5.1-konform, mit den korrekten Modul-Signaturen, sauberer Trennung von Umgebungsvariablen und ${env.NAME}, einem getesteten HTTP-Service und einem Fehlerpfad, der immer einen definierten String liefert. Weiter mit E5 — Teams-Bot einrichten.
✓ Das hast du jetzt verinnerlicht
Sitzt die ScriptEngine?
7 Fragen aus den Stufen 1–7. Kein Zertifikat — zur Selbstkontrolle. Beliebig oft wiederholbar.