AWS Lambda y API Gateway: Crea tu primera API (consola y CLI)
Los comandos de este post son bash. En Windows usa WSL, Git Bash o CloudShell, que ya viene con el CLI configurado. En PowerShell el heredoc, el JSON con comillas simples y
zipno funcionan igual.
Antes de empezar
Si usas tu cuenta personal de AWS con un usuario administrador, salta esta sección. Ya tienes todo.
Si estás en una cuenta de empresa, compartida, o en una de la universidad, es donde aparecen los AccessDenied.
Primero, mira con qué usuario estás autenticado.
aws sts get-caller-identityEste tutorial toca cuatro servicios, y cada uno necesita permisos:
| Para | Necesitas |
|---|---|
| Crear el rol de ejecución | IAMFullAccess |
| Crear e invocar la Lambda | AWSLambda_FullAccess |
| Crear la API | AmazonAPIGatewayAdministrator |
| Leer y borrar logs | CloudWatchLogsFullAccess |
Son policies gestionadas por AWS. Verifica los nombres exactos en tu consola antes de pedirlas, porque algunas cambiaron de nombre con los años.
IAMFullAccess es mucho poder. Quien la tiene puede crearse los permisos que quiera, o sea que en la práctica es acceso de administrador. Para aprender está bien. Para un usuario de producción, no.
Crea la función Lambda
Debemos ir al servicio AWS Lambda y crear nuestra primera función.

Tenemos 3 opciones:
- Author from scratch: Comenzar con un "Hello World".
- Use a blueprint: Una plantilla o configuración preestablecida.
- Container image: Con una imagen usando el servicio de ECR.
Voy a utilizar la opción de Author from scratch para simplemente hacer un Hello World.
Elegimos un nombre para la función y el runtime, o sea, el lenguaje en el que se va a ejecutar. Hay opciones como Node.js, Python, Java, Ruby, etc.

Ya con esto le podemos dar en Create function. Una vez creado veremos las siguientes pestañas:

En la pestaña de Code debemos escribir el código que se ejecutará cada vez que se llame a la función. Ten en cuenta que las Lambdas se cobran por dos cosas: el número de invocaciones y los GB-segundo (duración × memoria asignada).
El Always Free de AWS ofrece 1 millón de peticiones y 400,000 GB-segundos de cómputo cada mes. AWS Lambda pricing.
Lo mismo con el AWS CLI
La consola hace una cosa por debajo que el CLI no, y es crear el rol de ejecución. Así que empezamos por ahí.
# Guarda estos valores, los usaremos en todos los comandos
ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text)
REGION=$(aws configure get region || echo "$AWS_REGION")Revisa que ACCOUNT_ID y $REGION no estén vacíos.
echo "$ACCOUNT_ID / $REGION". Ten en cuenta que todos estos comandos van en la misma terminal. Si la cierras, las variables se pierden.
aws iam create-role \
--role-name lambda-hello-role \
--assume-role-policy-document '{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Principal": { "Service": "lambda.amazonaws.com" },
"Action": "sts:AssumeRole"
}]
}'
aws iam attach-role-policy \
--role-name lambda-hello-role \
--policy-arn arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRoleSi te sale un
AccessDeniedal crear el rol, no es que el rol no exista: es que tu usuario no tiene permiso para crear roles. El mensaje completo te dice cuál falta (is not authorized to perform: iam:CreateRole). Pasa mucho en cuentas de empresa o compartidas. Necesitas permisos de IAM, o que alguien con acceso de administrador te cree el rol.
Esa policy es la que permite escribir logs en CloudWatch. Sin ella la función corre, pero no verás nada.
Crea un archivo index.mjs con este contenido:
export const handler = async (event) => {
return {
statusCode: 200,
body: "Hello from Lambda!",
};
};Y empaquétalo con zip:
zip function.zip index.mjsaws lambda create-function \
--function-name hello-lambda \
--runtime nodejs22.x \
--handler index.handler \
--role arn:aws:iam::$ACCOUNT_ID:role/lambda-hello-role \
--zip-file fileb://function.zipSi el comando falla con un error de rol inválido, espera unos segundos y repítelo. IAM puede tardar en propagar el rol que creamos.
Y para probarla debemos ejecutar el siguiente comando:
aws lambda invoke --function-name hello-lambda response.json
cat response.jsonLos runtimes disponibles cambian y los viejos se deprecan. Revisa cuál está vigente antes de copiar
nodejs22.x, puedes verlos en Lambda runtimes.
Para subir cambios de código después:
zip function.zip index.mjs
aws lambda update-function-code \
--function-name hello-lambda \
--zip-file fileb://function.zipLogs de las Lambdas
Para ver cómo está funcionando tu Lambda, entra a la pestaña Monitor. Si quieres ver los logs completos, ve a CloudWatch, donde podrán ver cada evento que ocurra como lo pueden ver en este video:
Lo mismo con el AWS CLI
Este comando te muestra los logs en vivo sin abrir la consola:
aws logs tail /aws/lambda/hello-lambda --followPara ver solo lo reciente:
aws logs tail /aws/lambda/hello-lambda --since 10mAPI Gateway
Ahora vamos al servicio API Gateway y damos en Create an API.

Existen varios tipos que podemos usar:
- HTTP API
- WebSocket API
- REST API
- REST API Private
Usa HTTP API, es la v2, más simple y cuesta $1 por millón de requests contra $3.50 del REST API. Cambia a REST API solo si necesitas API keys, throttling por cliente, WAF o caching en el gateway. Verifica precios en los docs antes de publicar, además el precio depende de la región y por el tramo de volumen. Te recomiendo que leas Choose between REST APIs and HTTP APIs y Amazon API Gateway pricing.
Creación de la API Gateway
- Le damos un nombre y el tipo de IP address type (IPv4 o Dualstack).

- Definimos un stage, por ejemplo v1 o prod.

Un stage es una versión desplegada y con nombre de tu API. Cada una tiene su propia URL (.../v1, .../prod), su propia config (variables, logs, throttling) y apunta a un deployment distinto.
Lo mismo con el AWS CLI
Ojo con el subcomando: HTTP API es apigatewayv2, no apigateway. Si usas el segundo estás creando una REST API.
API_ID=$(aws apigatewayv2 create-api \
--name hello-api \
--protocol-type HTTP \
--query ApiId --output text)
echo $API_IDEl stage no se crea solo por CLI. Hay que pedirlo, y con --auto-deploy para no tener que desplegar a mano cada cambio:
aws apigatewayv2 create-stage \
--api-id $API_ID \
--stage-name v1 \
--auto-deployConectar nuestra Lambda
Ahora en la sección de Routes del API Gateway creamos una ruta, por ejemplo GET /hello.

Una vez creada la ruta debemos conectarla con nuestra Lambda a través de las Integrations. La integración es el recurso de backend al que esa ruta llama cuando recibe una solicitud.

Existen varias opciones de integraciones, en nuestro caso debemos seleccionar la Lambda Function.

Y ya solo queda seleccionar nuestra Lambda y darle en crear.

Y listo. Ahora si vamos a nuestra ruta /hello ya nos saldrá el "Hello from Lambda!" que habíamos definido en nuestra Lambda.
La URL del API Gateway la podemos ver en el menú izquierdo donde dice API, o en la parte de Stages seleccionando un stage.
Lo mismo con el AWS CLI
Aquí son tres comandos, y el tercero es el que a todo el mundo se le olvida.
Primero la integración:
INTEGRATION_ID=$(aws apigatewayv2 create-integration \
--api-id $API_ID \
--integration-type AWS_PROXY \
--integration-uri arn:aws:lambda:$REGION:$ACCOUNT_ID:function:hello-lambda \
--payload-format-version 2.0 \
--query IntegrationId --output text)--payload-format-version 2.0 no es opcional en la práctica. Si lo omites, el CLI usa 1.0 y el event que recibe tu Lambda tiene otra forma que la que ves en la consola.
Luego la ruta, apuntando a esa integración:
aws apigatewayv2 create-route \
--api-id $API_ID \
--route-key "GET /hello" \
--target integrations/$INTEGRATION_IDY el permiso. La consola lo agrega sola cuando eliges la Lambda en el dropdown; por CLI no. Si te lo saltas, la ruta responde 500 y los logs de la Lambda están vacíos, porque la Lambda nunca se llegó a ejecutar:
aws lambda add-permission \
--function-name hello-lambda \
--statement-id apigw-invoke-hello \
--action lambda:InvokeFunction \
--principal apigateway.amazonaws.com \
--source-arn "arn:aws:execute-api:$REGION:$ACCOUNT_ID:$API_ID/*/GET/hello"Para obtener la URL y probar:
ENDPOINT=$(aws apigatewayv2 get-api --api-id $API_ID \
--query ApiEndpoint --output text)
curl $ENDPOINT/v1/helloEl /v1 es el nombre del stage. Si tu stage se llama $default, la URL va sin prefijo.
CORS
El curl funciona. Pero un fetch() desde el navegador, no.
Esto es porque el API Gateway no manda las cabeceras de CORS por defecto, así que el navegador bloquea la respuesta. El error que ves en la consola del navegador no menciona API Gateway, y ahí es donde perdemos tiempo buscando el error.
En la consola está en el menú izquierdo, en la sección CORS de tu API.

Lo mismo con el AWS CLI
aws apigatewayv2 update-api --api-id $API_ID \
--cors-configuration AllowOrigins="*",AllowMethods="GET"AllowOrigins="*" sirve para probar. En algo real pon tu dominio.
Limpiar todo
Acabas de dejar una URL pública, sin autenticación, que ejecuta tu código. Cualquiera que la tenga puede invocarla. Este post no cubre autenticación, así que lo mejor es borrarlo.
Parado no te cuesta nada, pero borrar lo que ya no usas es una costumbre que te recomiendo.

El rol no se borra mientras tenga la policy adjunta.
aws apigatewayv2 delete-api --api-id $API_ID
aws lambda delete-function --function-name hello-lambda
aws iam detach-role-policy \
--role-name lambda-hello-role \
--policy-arn arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
aws iam delete-role --role-name lambda-hello-roleFalta uno. delete-function no borra los logs, y el log group se queda ahí:
aws logs delete-log-group --log-group-name /aws/lambda/hello-lambdaFrequently asked questions
¿Por qué mi Lambda tarda tanto la primera vez y luego va rápida?
Eso es un cold start. En la primera llamada, Lambda tiene que descargar tu código, arrancar el runtime y ejecutar todo lo que está fuera del handler. Después mantiene ese entorno vivo y lo reutiliza, así que las siguientes llamadas se saltan todo eso.
Lo puedes ver en los logs. La línea REPORT muestra Init Duration solo cuando hubo cold start.
¿Cómo instalo dependencias? En la consola no hay npm install
Correcto, no lo hay. En cuanto necesitas un paquete, subes un zip.
Tienes tres opciones:
- Zip. Instalas en local, comprimes tu código junto con
node_modulesy lo subes. Empieza por aquí. - Layers. Un zip aparte que le adjuntas a la función. Sirve cuando varias funciones comparten las mismas dependencias. Para una sola función no vale la pena.
- Imagen de container. Una imagen de Docker en ECR. Para dependencias pesadas, como librerías de machine learning.
Mi Lambda hace timeout. ¿Cuál es el límite?
Por defecto son 3 segundos. Ese es el que agarra a todo el mundo. El máximo son 15 minutos.
aws lambda update-function-configuration \
--function-name hello-lambda \
--timeout 30
Detrás de un HTTP API el techo real es más bajo. API Gateway corta la conexión a los 30 segundos y devuelve un 504, sin importar lo que diga el timeout de tu Lambda. Un REST API sí puede subir de ahí, pero solo los regionales y privados, y hay que pedirlo por Service Quotas. Los HTTP API no pueden.
Si necesitas más tiempo, no pelees con el límite. Responde 202 de inmediato y haz el trabajo en segundo plano.
¿Cuánta memoria le pongo? ¿Más memoria es más caro?
La memoria también define la CPU. Van juntas y no se configuran por separado. Alrededor de los 1.769 MB tienes una vCPU completa. Por debajo, una fracción.
Así que más memoria no siempre es más caro. Pagas GB-segundos, que es memoria por duración. Si al doblar la memoria la función termina en la mitad del tiempo, la factura es la misma y la respuesta llega antes.
Comments