Développer la fonction AEM Edge

IMPORTANT
AEM Edge Functions est actuellement en version bêta. Les fonctionnalités et la documentation peuvent changer. Pour tout commentaire, contactez 🔗.

Notre objectif est de créer un bloc Edge Delivery Services dynamique qui appelle une fonction AEM Edge pour récupérer des données dynamiques à partir d’une API tierce.

La première étape consiste à développer la fonction AEM Edge , qui expose un point d’entrée qui combine deux appels d’API en amont en une seule réponse et active CORS pour le développement local. Pour les notions de base du contrat de requête et de réponse, de la correspondance des points d’entrée et de la fetch() sortante, voir Créer un point d’entrée d’API avec des fonctions Edge. Seules les parties spécifiques à ce tutoriel sont abordées ici.

Commencer à partir de zéro

Votre projet de fonctions AEM Edge (cloné dans Configurer les fonctions AEM Edge sur Edge Delivery Services) est fourni avec deux exemples d’itinéraires, /hello-world et /weather. Vous disposez donc d’un élément à tester dès le premier jour. Aucun de ces composants n’appartient à un projet réel. Examinez d’abord le standard et supprimez ce dont vous n’avez pas besoin.

Définition du contrat d’API

Avant d’écrire le gestionnaire, définissez la forme de la requête et de la réponse afin que le bloc Edge Delivery Services ou tout autre appelant puisse être créé par rapport au contrat sans lire la mise en œuvre.

Point d’entrée : GET /api/frescopa/estimated-delivery

GET /api/frescopa/estimated-delivery?sku=house-blend-medium-roast&postcode=90210

postcode est obligatoire. sku est facultatif et la valeur par défaut est house-blend-medium-roast.

Corps de la réponse :

Une réponse réussie renvoie la forme suivante :

// 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"
}

Les réponses d’erreur partagent cette forme, avec un code différent pour chaque raison d’échec :

// 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"
}

Chaque corps d’erreur inclut un champ de code, de sorte qu’un appelant peut se brancher sur la raison de l’échec au lieu d’analyser les message. OPTIONS requêtes obtiennent une réponse distincte pour le contrôle en amont CORS, traité dans la section ​ Activer CORS pour le développement local ​ ci-dessous.

Implémenter la logique commerciale

Limitez les index.js au routage. La logique commerciale se trouve sous src/handlers/, un dossier par point d’entrée.

Fichiers que vous ajouterez

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

Gestionnaire et appels en amont

Le gestionnaire dans handler.js prend un sku et un postcode, appelle les deux services en amont et fusionne leurs résultats dans une seule réponse :

// 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 exclut les formes JSON d’erreur et de succès du gestionnaire et constants.js contient le chemin d’itinéraire et le SKU par défaut, de sorte que handler.js reste concentré sur la séquence de deux appels. Chaque simulation dans src/mocks/ charge son propre jeton avant de renvoyer des données, de la même forme qu’un client d’API réel utiliserait :

// 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() et authorizedFetch() vivent en lib/api-client.js. product-api.js et delivery-api.js partagent cet helper, de sorte que chaque API en amont conserve sa propre clé de magasin secrète (CATALOG_API_TOKEN, DELIVERY_API_TOKEN) sans dupliquer la logique d’authentification. Lorsque vous êtes prêt à remplacer un appel simulé par une API réelle, suivez la section Utiliser des configurations et des secrets avec des fonctions Edge pour la configuration des secrets sur un site déployé, puis supprimez les commentaires du bloc ci-dessus.

Chaque plateforme limite un seul appel à des appels de récupération sortante 32, de sorte que deux appels ici laissent beaucoup de marge de manœuvre.

Activer CORS pour le développement local

Le serveur de développement local du bloc Edge Delivery Services s’exécute sur http://localhost:3000. Le serveur de développement local de la fonction AEM Edge s’exécute sur http://127.0.0.1:7676. Il s’agit de deux origines différentes. Donc, sans en-têtes CORS, le navigateur bloque la requête avant même que votre code ne la voie.

Vérifiez l’origine de la requête par rapport à une liste autorisée dans cors.js, puis appliquez la même vérification à la requête de contrôle en amont OPTIONS que le navigateur envoie en premier :

// 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);

L’implémentation de référence étend la ALLOWED_ORIGIN_EXACT avec une expression régulière pour autoriser également les domaines de développement, d’évaluation et de production déployés, car une requête vers ces domaines est de même origine via le réseau CDN et n’a techniquement pas besoin d’un en-tête CORS, mais leur correspondance rend explicitement la liste autorisée auto-documentée. Une fois déployé, le navigateur n’envoie jamais de vérification CORS pour une requête de même origine, de sorte que l’en-tête supplémentaire est sans danger. Vous pouvez laisser l’assistant CORS en place ; il ajoute uniquement un en-tête lorsque l’origine de la requête correspond à la liste autorisée.

Configuration du serveur de développement local dans fastly.toml

Le fastly.toml configure le serveur de développement local démarré par aio aem edge-functions serve. Pour ce tutoriel, son bloc de [local_server.secret_stores] donne à chaque API en amont un jeton local, de sorte que le gestionnaire peut SecretStoreManager.getSecret() appeler sans vraies informations d’identification :

# 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"

Le standard déclare également une entrée [local_server.backends] pour l’API Open-Meteo de l’échantillon météorologique. Comme ce tutoriel simule les deux appels en amont au lieu d’appeler un hôte réel, supprimez cette entrée au lieu de la rediriger.

Pour l’ensemble complet des options de [local_server], voir la référence Fastly.toml.

Tester le point d’entrée localement

À ce stade, vous testez uniquement le contrat d’API : la forme de requête et de réponse du point d’entrée, indépendante du bloc Edge Delivery Services qui l’appellera ultérieurement.

Démarrez le serveur de développement local à partir de Configuration des fonctions AEM Edge sur Edge Delivery Services :

$ aio aem edge-functions serve

Appelez le chemin heureux avec curl ou ouvrez l’URL dans un navigateur :

$ curl "http://127.0.0.1:7676/api/frescopa/estimated-delivery?sku=house-blend-medium-roast&postcode=90210"

Confirmez que la réponse fusionne les données des deux appels en amont, par exemple un nom de produit de la recherche de catalogue et une estimation de diffusion de la recherche d’exécution dans le même corps JSON.

Serveur de développement local répondant au point d’entrée de diffusion estimé

Confirmez ensuite les chemins d’accès d’erreur responses.js les versions :

# 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"

Chaque doit renvoyer un corps d’erreur JSON avec un champ code (MISSING_POSTCODE, UNKNOWN_SKU) plutôt qu’un 500 générique, confirmant que le gestionnaire valide l’entrée avant d’atteindre les appels en amont.

Ajouter le point d’entrée

Deux fichiers de configuration déclarent l’itinéraire, en suivant le modèle dans Créer un point d’entrée d’API.

Le config/edgeFunctions.yaml déclare la fonction AEM Edge elle-même, inchangée par rapport à la norme :

# config/edgeFunctions.yaml
kind: "EdgeFunctions"
version: "1"
data:
  functions:
    - name: my-edge-function

Le config/cdn.yaml ajoute une règle de sélecteur d’origine qui transfère le chemin d’accès de ce tutoriel à cette fonction :

# 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

Consultez les fichiers complets dans l’implémentation de référence : config/edgeFunctions.yaml et config/cdn.yaml.

Étapes suivantes

Dans Développement du bloc Edge Delivery Services, vous mettez à niveau le bloc qui appelle ce point d’entrée, puis vous le créez dans l’éditeur universel.

Ressources supplémentaires

recommendation-more-help
experience-manager-learn-help-cloud-service