Desenvolver a função Edge do AEM
Nossa meta é criar um bloco dinâmico do Edge Delivery Services que chame uma Função AEM Edge para buscar dados dinâmicos de uma API de terceiros.
A primeira etapa é desenvolver a função Edge do AEM, que expõe um terminal que combina duas chamadas de API upstream em uma resposta e permite o CORS para desenvolvimento local. Para obter as noções básicas do contrato de solicitação e resposta, correspondência de ponto de extremidade e saída fetch(), consulte Criar um ponto de extremidade de API com funções do Edge. Somente as partes específicas deste tutorial são abordadas aqui.
Iniciar com base na tabela
Seu projeto do AEM Edge Functions (clonado em Configurar o AEM Edge Functions no Edge Delivery Services) vem com duas rotas de exemplo, /hello-world e /weather, então você tem algo funcionando para testar no primeiro dia. Nenhum deles pertence a um projeto real, portanto, revise primeiro o modelo e remova o que não é necessário.
Definir o contrato de API
Antes de gravar o manipulador, defina a solicitação e a forma de resposta, para que o bloco do Edge Delivery Services ou qualquer outro chamador possa ser criado no contrato sem ler a implementação.
Ponto de extremidade: GET /api/frescopa/estimated-delivery
GET /api/frescopa/estimated-delivery?sku=house-blend-medium-roast&postcode=90210
postcode é obrigatório. sku é opcional e seu padrão é house-blend-medium-roast.
Corpo da resposta:
Uma resposta bem-sucedida retorna esta forma:
// 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"
}
As respostas de erro compartilham esta forma, com um code diferente para cada motivo de falha:
// 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"
}
Todo corpo de erro inclui um campo code, portanto, um chamador pode ramificar o motivo da falha em vez de analisar message. OPTIONS solicitações recebem uma resposta separada para a comprovação do CORS, abordada em Habilitar CORS para desenvolvimento local abaixo.
Implementar a lógica de negócios
Mantenha index.js limitado ao roteamento. A lógica comercial está em src/handlers/, uma pasta por ponto de extremidade.
Arquivos que você adicionará
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
Manipulador e chamadas upstream
O manipulador em handler.js pega um sku e um postcode, chama os dois serviços upstream e mescla seus resultados em uma resposta:
// 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 mantém as formas JSON de erro e sucesso fora do manipulador, e constants.js mantém o caminho da rota e o SKU padrão, então handler.js permanece focalizado na sequência de duas chamadas. Cada modelo em src/mocks/ carrega seu próprio token antes de retornar os dados, da mesma forma que um cliente de API real usaria:
// 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() e authorizedFetch() vivem em lib/api-client.js. O product-api.js e o delivery-api.js compartilham esse auxiliar, de modo que cada API de upstream mantém sua própria chave de Repositório Secreto (CATALOG_API_TOKEN, DELIVERY_API_TOKEN) sem duplicar a lógica de autenticação. Quando estiver pronto para substituir uma chamada simulada por uma API real, siga Usar configurações e segredos com Funções Edge para a configuração secreta em um site implantado e remova o comentário do bloco acima.
Cada plataforma limita uma única invocação a 32 chamadas de busca de saída, portanto, duas chamadas aqui deixam bastante espaço.
Habilitar CORS para desenvolvimento local
O servidor de desenvolvimento local do bloco Edge Delivery Services é executado em http://localhost:3000. O servidor de desenvolvimento local da AEM Edge Function é executado em http://127.0.0.1:7676. Essas são duas origens diferentes, portanto, sem os cabeçalhos CORS, o navegador bloqueia a solicitação antes que o código a veja.
Verifique a origem da solicitação em relação a uma lista de permissões em cors.js e aplique a mesma verificação à solicitação de comprovação OPTIONS que o navegador envia primeiro:
// 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);
A implementação de referência estende o ALLOWED_ORIGIN_EXACT com um regex para também permitir os domínios de desenvolvimento, preparo e produção implantados, já que uma solicitação para esses domínios tem a mesma origem por meio da CDN e tecnicamente não precisa de um cabeçalho CORS, mas correspondê-los explicitamente torna a lista de permissões autodocumentável. Depois de implantado, o navegador nunca envia uma verificação do CORS para uma solicitação de mesma origem, portanto, o cabeçalho extra é inofensivo. Você pode deixar o auxiliar do CORS no lugar; ele só adiciona um cabeçalho quando a origem da solicitação corresponde à lista de permissões.
Configurar o servidor de desenvolvimento local em fastly.toml
O fastly.toml configura o servidor de desenvolvimento local iniciado por aio aem edge-functions serve. Para este tutorial, seu bloco [local_server.secret_stores] fornece a cada API upstream um token local, de modo que o manipulador pode chamar SecretStoreManager.getSecret() sem credenciais reais:
# 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"
O padrão de formatação também declara uma entrada [local_server.backends] para a API Open-Meteo de amostra meteorológica. Como este tutorial simula ambas as chamadas upstream em vez de chamar um host real, remova essa entrada em vez de redirecioná-la.
Para obter o conjunto completo de opções de [local_server], consulte a referência de Fastly.toml.
Testar o ponto de extremidade localmente
Neste ponto, você está testando somente o contrato de API: a solicitação do ponto de extremidade e a forma de resposta, independentemente do bloco do Edge Delivery Services que o chamará posteriormente.
Iniciar o servidor de desenvolvimento local em Configurar Funções do AEM Edge no Edge Delivery Services:
$ aio aem edge-functions serve
Chame o caminho feliz com curl ou abra a URL em um navegador:
$ curl "http://127.0.0.1:7676/api/frescopa/estimated-delivery?sku=house-blend-medium-roast&postcode=90210"
Confirme a resposta mescla dados de ambas as chamadas upstream, por exemplo, um nome de produto da pesquisa de catálogo e uma estimativa de delivery da pesquisa de preenchimento no mesmo corpo JSON.
Em seguida, confirme os caminhos de erro responses.js compilações:
# 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"
Cada um deve retornar um corpo de erro JSON com um campo code (MISSING_POSTCODE, UNKNOWN_SKU) em vez de um 500 genérico, confirmando que o manipulador valida a entrada antes que ela atinja as chamadas upstream.
Adicionar o endpoint
Dois arquivos de configuração declaram a rota, seguindo o padrão em Criar um ponto de extremidade de API.
O config/edgeFunctions.yaml declara a própria Função Edge do AEM, inalterada a partir do padrão padrão:
# config/edgeFunctions.yaml
kind: "EdgeFunctions"
version: "1"
data:
functions:
- name: my-edge-function
O config/cdn.yaml adiciona uma regra de seletor de origem que encaminha o caminho deste tutorial para essa função:
# 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
Consulte os arquivos completos na implementação de referência: config/edgeFunctions.yaml e config/cdn.yaml.
Próximas etapas
Em Desenvolver o bloco do Edge Delivery Services, você cria o bloco que chama esse ponto de extremidade e o cria no Editor Universal.