API Gateway: treure el Lambda a internet
La funció de la lliçó anterior només la pots invocar tu, amb credencials d'AWS. Per obrir-la al món hi ha tres camins, i triar malament et costa diners o feina.
| Opció | Preu per milió | Quan la vols |
|---|---|---|
| Function URL | 0 $ | Un webhook intern, una prova, res de públic |
| HTTP API | 1,00 $ | El 90 % dels casos |
| REST API | 3,50 $ | Necessites claus d'API, plans d'ús, WAF o transformacions |
La REST API és tres vegades i mitja més cara i porta vint anys de funcionalitats que probablement no faràs servir. Comença sempre per HTTP API; si algun dia et falta alguna cosa, ja canviaràs.
Muntar-la amb quatre comandes
COMPTE=$(aws sts get-caller-identity --query Account --output text)
REGIO=eu-west-1
API_ID=$(aws apigatewayv2 create-api \
--name hola-api --protocol-type HTTP \
--query ApiId --output text --region $REGIO)
INTEG_ID=$(aws apigatewayv2 create-integration \
--api-id "$API_ID" \
--integration-type AWS_PROXY \
--integration-uri "arn:aws:lambda:${REGIO}:${COMPTE}:function:hola" \
--payload-format-version 2.0 \
--query IntegrationId --output text --region $REGIO)
aws apigatewayv2 create-route \
--api-id "$API_ID" \
--route-key 'GET /hola' \
--target "integrations/${INTEG_ID}" --region $REGIO
aws apigatewayv2 create-stage \
--api-id "$API_ID" --stage-name '$default' --auto-deploy --region $REGIO
Falta la peça que oblida tothom. API Gateway no té permís per invocar la teva funció només perquè els hagis connectat; el permís viu al costat de la Lambda, en una política de recursos:
aws lambda add-permission \
--function-name hola \
--statement-id apigw-hola \
--action lambda:InvokeFunction \
--principal apigateway.amazonaws.com \
--source-arn "arn:aws:execute-api:${REGIO}:${COMPTE}:${API_ID}/*/*/hola" \
--region $REGIO
Ara ja tens URL pública:
curl "https://${API_ID}.execute-api.${REGIO}.amazonaws.com/hola"
L'etapa $default és el que fa que la URL no porti prefix. Si li dius prod, la ruta passa a ser /prod/hola i te'l menges per sempre a totes les integracions.
Què li arriba realment a la funció
Amb payload-format-version 2.0, l'esdeveniment té aquesta forma. És diferent de la 1.0, i mesclar-les és la font d'errors número u:
// src/index.ts
import type { APIGatewayProxyEventV2 } from "aws-lambda";
export const handler = async (event: APIGatewayProxyEventV2) => {
const metode = event.requestContext.http.method; // NO event.httpMethod
const ruta = event.rawPath; // NO event.path
const nom = event.queryStringParameters?.nom ?? "món";
const cos = event.body ? JSON.parse(event.body) : null;
return {
statusCode: 200,
headers: { "content-type": "application/json; charset=utf-8" },
body: JSON.stringify({ missatge: `Hola, ${nom}`, metode, ruta, cos }),
};
};
Dues coses del format 2.0 que et faran perdre una tarda:
event.bodysempre és una cadena, mai un objecte. Si el client envia JSON, el parseges tu. I siisBase64Encodedéstrue, primer el descodifiques.- Si retornes un objecte sense
statusCode, API Gateway l'interpreta com a cos i respon 200 amb el JSON sencer. Sembla màgia i va bé per a proves, però et deixa sense manera de retornar un 404. Retorna sempre l'objecte complet.
Els paràmetres de ruta funcionen amb claus entre claudàtors:
aws apigatewayv2 create-route --api-id "$API_ID" \
--route-key 'GET /usuaris/{id}' \
--target "integrations/${INTEG_ID}" --region $REGIO
i els llegeixes a event.pathParameters.id.
CORS
Si l'API la crida un navegador des d'un altre domini, necessites CORS. Es configura a l'API, no a la funció:
aws apigatewayv2 update-api --api-id "$API_ID" --region $REGIO \
--cors-configuration \
AllowOrigins="https://formacio.joansanfeliu.com",\
AllowMethods="GET,POST,OPTIONS",\
AllowHeaders="content-type",\
MaxAge=3600
Regla: o ho fas aquí, o ho fas als headers de la Lambda. Mai les dues coses. Si poses Access-Control-Allow-Origin als dos llocs, el navegador rep la capçalera duplicada i la petició falla amb un missatge que no menciona enlloc que el problema és una duplicació.
Amb la configuració a l'API, les peticions OPTIONS de preflight les respon API Gateway sol i no arriben mai a la teva funció. Això, a més de ser correcte, t'estalvia la meitat de les invocacions.
El límit dels 30 segons
Una integració d'HTTP API talla als 30 segons. No és configurable, no hi ha quota que demanar, i el client rep un 504.
No és un problema petit: vol dir que qualsevol cosa que pugui trigar més d'aquest temps —generar un PDF gros, cridar un model d'IA, processar un fitxer que t'han pujat— no pot viure darrere d'una petició HTTP síncrona. El patró correcte és respondre 202 de seguida amb un identificador, fer la feina en una altra Lambda invocada de manera asíncrona o per cua, i que el client consulti l'estat. Val més descobrir-ho ara que quan ho tinguis en producció.
Què costa això
| Concepte | Preu | Nivell gratuït |
|---|---|---|
| HTTP API | 1,00 $ per milió de peticions (primers 300M) | 1M/mes durant 12 mesos |
| REST API | 3,50 $ per milió (primers 333M) | 1M/mes durant 12 mesos |
| Function URL | 0 $ | — |
| Transferència de dades cap a fora | ~0,09 $ per GB | 100 GB/mes |
Un exemple real: una API amb 500.000 peticions al mes, cada una amb una Lambda de 256 MB i 150 ms, et surt per 0,50 $ d'API Gateway + 0,10 $ de Lambda. Seixanta cèntims. Aquí no és on tens el problema de costos.
On sí que el tens: transferència de dades. Si serveixes imatges o fitxers a través de l'API, els 0,09 $/GB de sortida es mengen tota la resta. Els fitxers estàtics no passen mai per API Gateway; van a S3 amb CloudFront al davant, que és la lliçó 05.
Errors habituals
{"message":"Not Found"}amb 404. La clau de ruta no coincideix.GET /holano atén/Holani/hola/. Comprova-ho ambaws apigatewayv2 get-routes --api-id "$API_ID".- 500 «Internal Server Error» i la Lambda sense cap log. Si al grup de logs de la funció no hi ha absolutament res, la petició no hi ha arribat mai: t'has deixat el
add-permission. És el motiu més freqüent de tots. - 502 «Bad Gateway». La funció ha respost, però amb una forma que API Gateway no entén: un
bodyque és un objecte en comptes d'una cadena, o unstatusCodeen text. Serialitza sempre ambJSON.stringify. event.pathésundefined. Estàs llegint el format 1.0 en una integració 2.0. Els tipus de@types/aws-lambdat'ho haurien dit: fes servirAPIGatewayProxyEventV2i deixa que el compilador treballi per tu.
T'aviso quan publiqui
Un correu per setmana com a molt, i només quan publiqui alguna cosa. De tant en tant t'hi explico el que faig: cursos, sessions, producte. Te'n pots donar de baixa quan vulguis, amb un clic.