Entwickeln der AEM Edge-Funktion
Unser Ziel ist es einen Edge Delivery Services-Block zu erstellen, der eine AEM Edge-Funktion aufruft, um dynamische Daten aus einer Drittanbieter-API abzurufen.
Der erste Schritt besteht in der Entwicklung der AEM Edge Function , die einen Endpunkt verfügbar macht, der zwei Upstream-API-Aufrufe zu einer Antwort kombiniert und CORS für die lokale Entwicklung aktiviert. Die Grundlagen zu Anfrage- und Antwortverträgen, Endpunktvergleichen und ausgehenden fetch() finden Sie unter Erstellen eines API-Endpunkts mit Edge-Funktionen. Nur die für dieses Tutorial spezifischen Teile werden hier behandelt.
Vom Boilerplate aus starten
Ihr AEM Edge Functions-Projekt (geklont in Einrichten von AEM Edge-Funktionen auf Edge Delivery Services) wird mit zwei Beispielrouten, /hello-world und /weather, ausgeliefert, sodass Sie am ersten Tag etwas zum Testen haben. Keines von beiden gehört in ein echtes Projekt, also überprüfen Sie zunächst das Textbaustein und entfernen Sie, was Sie nicht benötigen.
Definieren des API-Vertrags
Definieren Sie vor dem Schreiben des Handlers die Anfrage- und Antwort-Form, damit der Edge Delivery Services-Block oder ein anderer Aufrufer für den Vertrag erstellt werden kann, ohne die Implementierung zu lesen.
Endpunkt: GET /api/frescopa/estimated-delivery
GET /api/frescopa/estimated-delivery?sku=house-blend-medium-roast&postcode=90210
postcode ist erforderlich. sku ist optional und standardmäßig auf house-blend-medium-roast festgelegt.
Antworttext:
Eine erfolgreiche Antwort gibt diese Form zurück:
// 200 OK
{
"sku": "house-blend-medium-roast",
"productName": "House Blend - Medium Roast",
"inventoryStatus": "in-stock", // or "low-stock", "out-of-stock"
"qtyLeft": 12,
"fulfillmentRegion": "West Coast Fulfillment",
"deliveryEta": "tomorrow",
"cutoffMessage": "Order by 2:00 PM for same-day dispatch.",
"message": "Arrives tomorrow in West Coast Fulfillment"
}
Fehlerantworten haben diese Form gemeinsam, wobei für jeden Fehlergrund ein anderer code vorhanden ist:
// 400 missing postcode, 404 unknown sku, 405 wrong method: same shape, different code
{
"error": "Missing required parameter",
"code": "MISSING_POSTCODE", // or "UNKNOWN_SKU", "METHOD_NOT_ALLOWED"
"message": "The postcode query parameter is required"
}
Jeder Fehlertext enthält ein code Feld, sodass ein Aufrufer eine Verzweigung über die Fehlerursache erstellen kann, anstatt message zu analysieren. OPTIONS Anfragen erhalten eine separate Antwort für CORS Preflight, das unter Aktivieren von CORS für lokale Entwicklung unten beschrieben wird.
Geschäftslogik implementieren
Beschränken Sie index.js auf Routing. Die Geschäftslogik befindet sich unter src/handlers/, ein Ordner pro Endpunkt.
Dateien, die Sie hinzufügen werden
src/
├── index.js # routes to a handler, nothing else
├── handlers/estimated-delivery/
│ ├── handler.js # orchestrates the two upstream calls
│ ├── responses.js # builds the success and error JSON payloads
│ └── constants.js # route path and query-param defaults
├── lib/
│ ├── api-client.js # shared per-API token loading
│ └── cors.js # origin allow-list, applied to every response
└── mocks/ # tutorial stand-ins for the two upstream APIs
├── catalog/product-api.js
└── delivery/delivery-api.js
Handler- und Upstream-Aufrufe
Der Handler in handler.js nimmt ein sku und ein postcode, ruft die beiden Upstream-Services auf und führt ihre Ergebnisse in einer Antwort zusammen:
// src/handlers/estimated-delivery/handler.js
async function estimatedDeliveryBySkuAndPostcodeHandler(req) {
const url = new URL(req.url);
const sku = (url.searchParams.get("sku") ?? DEFAULT_SKU).trim();
const postcode = (url.searchParams.get("postcode") ?? "").trim();
if (!postcode) {
return missingPostcodeError(sku);
}
// API 1: catalog lookup
const product = await getProductBySku(sku);
if (!product) {
return unknownSkuError(sku, postcode);
}
// API 2: fulfillment lookup
const fulfillment = await getEstimatedDeliveryByPostcode(postcode);
return json(buildSuccessResponse(product, fulfillment));
}
responses.js bewahrt die JSON-Shapes für Fehler und Erfolg aus dem Handler auf und constants.js den Routenpfad und die Standard-SKU, sodass handler.js auf die Sequenz mit zwei Aufrufen fokussiert bleibt. Jede Nachahmung in lädt src/mocks/ ihr eigenes Token, bevor sie Daten zurückgibt. Dies entspricht der Form, die ein echter API-Client verwenden würde:
// src/mocks/catalog/product-api.js
export async function getProductBySku(sku) {
await getApiToken(SECRETS.CATALOG);
// Real API: uncomment and replace with your catalog endpoint.
// const response = await authorizedFetch(
// `https://api.example.com/products?sku=${encodeURIComponent(sku)}`,
// SECRETS.CATALOG,
// );
// return response.ok ? response.json() : null;
return PRODUCTS[sku] || null;
}
getApiToken() und authorizedFetch() leben in lib/api-client.js. Sowohl product-api.js als auch delivery-api.js verwenden diesen Helper gemeinsam, sodass jede Upstream-API ihren eigenen geheimen Speicherschlüssel (CATALOG_API_TOKEN, DELIVERY_API_TOKEN) beibehält, ohne die Authentifizierungslogik zu duplizieren. Wenn Sie bereit sind, einen simulierten Aufruf durch einen echten API-Aufruf zu ersetzen, folgen Sie Verwenden von Konfigurationen und Geheimnissen mit Edge-Funktionen für die geheime Einrichtung auf einer bereitgestellten Site und heben Sie dann die Auskommentierung des obigen Blocks auf.
Jede Plattform beschränkt einen einzelnen Aufruf auf 32 ausgehende Abrufaufrufe sodass zwei Aufrufe hier genügend Spielraum lassen.
Aktivieren von CORS für die lokale Entwicklung
Der lokale Dev-Server des Edge Delivery Services-Blocks wird auf http://localhost:3000 ausgeführt. Der lokale Dev-Server der AEM Edge-Funktion wird auf http://127.0.0.1:7676 ausgeführt. Dies sind zwei verschiedene Ursprünge, sodass der Browser ohne CORS-Header die Anfrage blockiert, bevor Ihr Code sie jemals sieht.
Überprüfen Sie den Ursprung der Anfrage mit einer Zulassungsliste in cors.js und wenden Sie dieselbe Prüfung auf die OPTIONS Preflight-Anfrage an, die der Browser zuerst sendet:
// src/lib/cors.js
const ALLOWED_ORIGIN_EXACT = new Set([
"http://localhost:3000",
"http://127.0.0.1:3000",
]);
function isAllowedOrigin(origin) {
return Boolean(origin) && ALLOWED_ORIGIN_EXACT.has(origin);
}
function applyCors(request, response) {
const origin = request.headers.get("Origin");
if (!isAllowedOrigin(origin)) {
return response;
}
const headers = new Headers(response.headers);
headers.set("access-control-allow-origin", origin);
headers.set("vary", "Origin");
return new Response(response.body, { status: response.status, headers });
}
function corsPreflightResponse(request) {
const origin = request.headers.get("Origin");
if (!isAllowedOrigin(origin)) {
return new Response(null, { status: 403 });
}
return new Response(null, {
status: 204,
headers: {
"access-control-allow-origin": origin,
"access-control-allow-methods": "GET, OPTIONS",
"access-control-allow-headers": "Content-Type",
vary: "Origin",
},
});
}
export { applyCors, corsPreflightResponse };
// src/index.js
if (url.pathname === "/api/frescopa/estimated-delivery") {
if (req.method === "OPTIONS") {
finalResponse = corsPreflightResponse(req);
} else if (req.method === "GET") {
finalResponse = await estimatedDeliveryBySkuAndPostcodeHandler(req);
}
}
finalResponse = applyCors(req, finalResponse);
Die Referenzimplementierung erweitert ALLOWED_ORIGIN_EXACT mit einem Regex, um auch die bereitgestellten Entwicklungs-, Staging- und Produktions-Domains zuzulassen, da eine Anfrage an diese Domains vom CDN aus identisch ist und technisch keinen CORS-Header benötigt, aber die Zuordnung zu diesen Domains macht die Zulassungsliste explizit selbstdokumentierend. Nach der Bereitstellung sendet der Browser nie eine CORS-Prüfung für eine Anfrage mit derselben Herkunft, sodass die zusätzliche -Kopfzeile harmlos ist. Sie können den CORS-Helper beibehalten. Er fügt nur dann eine Kopfzeile hinzu, wenn der Ursprung der Anfrage mit der Zulassungsliste übereinstimmt.
Konfigurieren des lokalen Dev-Servers in fastly.toml
Der fastly.toml konfiguriert den lokalen Dev-Server, der von aio aem edge-functions serve gestartet wird. In diesem Tutorial erhält jede Upstream-API durch ihren [local_server.secret_stores] ein lokales Token, sodass der Handler SecretStoreManager.getSecret() ohne echte Anmeldeinformationen aufrufen kann:
# fastly.toml
[local_server.secret_stores]
[[local_server.secret_stores.secret_default]]
key = "CATALOG_API_TOKEN"
data = "catalog-tutorial-token"
[[local_server.secret_stores.secret_default]]
key = "DELIVERY_API_TOKEN"
data = "delivery-tutorial-token"
In der Textvorlage wird auch ein [local_server.backends] für die Open-Meteo-API des Wetterbeispiels deklariert. Da dieses Tutorial beide Upstream-Aufrufe simuliert, anstatt einen echten Host aufzurufen, entfernen Sie diesen Eintrag, anstatt ihn neu zu verweisen.
Die vollständige Liste der [local_server] Optionen finden Sie in der Referenz Fastly.toml.
Lokales Testen des Endpunkts
An dieser Stelle testen Sie nur den API-Vertrag: die Anfrage- und Antwort-Form des Endpunkts, unabhängig vom Edge Delivery Services-Block, der ihn später aufruft.
Starten Sie den lokalen Dev-Server über Einrichten von AEM Edge-Funktionen in Edge Delivery Services:
$ aio aem edge-functions serve
Rufen Sie den Happy Path mit curl auf oder öffnen Sie die URL in einem Browser:
$ curl "http://127.0.0.1:7676/api/frescopa/estimated-delivery?sku=house-blend-medium-roast&postcode=90210"
Bestätigen Sie die Antwort, um Daten aus beiden Upstream-Aufrufen zusammenzuführen, z. B. einen Produktnamen aus der Katalogsuche und eine Versandschätzung aus der Erfüllungssuche im selben JSON-Hauptteil.
Bestätigen Sie dann die Fehlerpfade responses.js Builds:
# missing postcode
$ curl "http://127.0.0.1:7676/api/frescopa/estimated-delivery?sku=house-blend-medium-roast"
# unknown SKU
$ curl "http://127.0.0.1:7676/api/frescopa/estimated-delivery?sku=not-a-real-product&postcode=90210"
Jeder sollte einen JSON-Fehlertext mit einem code Feld (MISSING_POSTCODE, UNKNOWN_SKU) anstelle einer generischen 500 zurückgeben, um zu bestätigen, dass der Handler die Eingabe validiert, bevor sie die Upstream-Aufrufe erreicht.
Hinzufügen des Endpunkts
Zwei Konfigurationsdateien deklarieren die Route und folgen dem Muster in Erstellen eines API-Endpunkts.
Die config/edgeFunctions.yaml deklariert die AEM Edge-Funktion selbst, die gegenüber dem Textbaustein unverändert ist:
# config/edgeFunctions.yaml
kind: "EdgeFunctions"
version: "1"
data:
functions:
- name: my-edge-function
Der config/cdn.yaml fügt eine Regel zur Ursprungsauswahl hinzu, die den Pfad dieses Tutorials an diese Funktion weiterleitet:
# config/cdn.yaml
- name: route-estimated-delivery-to-edge-function
when: { reqProperty: path, equals: "/api/frescopa/estimated-delivery" }
action:
type: selectAemOrigin
originName: edgefunction-my-edge-function
skipCache: true
Die vollständigen Dateien finden Sie in der Referenzimplementierung: config/edgeFunctions.yaml und config/cdn.yaml.
Nächste Schritte
In Entwickeln des Edge Delivery Services-Blocks erstellen Sie eine Strukturvorlage für den Block, der diesen Endpunkt aufruft, und erstellen ihn dann im universellen Editor.