Módulo 4: De la consola a tu API
Tiempo estimado: 45 min
Lo que vas a construir
Primero creas una tabla de DynamoDB y una Lambda desde la consola, y compruebas que siguen separadas aunque vivan en el mismo proyecto. Después borras esa práctica y desplegamos la API de gastos completa con AWS CDK.
Parte 1: crea los recursos a mano (15 min)
Crea una tabla de DynamoDB
Te toca: crea la tabla de la práctica
- Abre DynamoDB > Tables en la consola y elige Create table.
- En Table name escribe
gastos-hormiga-practica-tabla. - En Partition key escribe
idy deja el tipo String. - Deja Default settings y crea la tabla.
Guarda y observa un item
Te toca: agrega un item a tu tabla
- En la tabla, abre Actions > Create item.
- En el atributo
id, deja el tipo String y escribeholacomo valor. - Elige Create item.
- Abre Explore table items, deja seleccionada la opción Scan y elige Run. Tienes que ver el item
hola.
Crea y prueba una Lambda
Te toca: crea la función de la práctica
- Abre Lambda > Functions y elige Create an Event Function.
- Selecciona Use a blueprint y elige Hello world function.
- Usa el nombre
gastos-hormiga-practica-funcion. - Confirma el runtime Node.js 22.x y la arquitectura x86_64.
- En Execution role, deja la configuración predeterminada. La consola crea el rol sola; no elijas Customize.
- Crea la función.
💡 Esta Lambda usa el blueprint de la consola. La app final de
infra/gastos-stack.tsusa Node.js 24.x y arm64. Aquí solo nos importa ver qué recursos crea la consola. La Lambda corre con el Node que AWS le da, no con el de tu laptop.
Te toca: prueba la función
- Elige Test, crea un evento llamado
practicacon el JSON por default y vuelve a elegir Test. Debe terminar en verde.

- Abre la pestaña Monitor y baja hasta CloudWatch logs para ver el log de la prueba.

Ese log group apareció solo al ejecutar la función. Hasta ahora creaste todo con clics, y la consola agregó por su cuenta un execution role y un log group. Si quisieras repetir esta app en otro proyecto, tendrías que acordarte de cada opción y de cada recurso.
Limpia la práctica
Te toca: borra los recursos manuales
npm run limpiar-practica
Revisa los cuatro nombres, escribe limpiar-practica para confirmar y espera el mensaje La práctica quedó limpia.
El comando solo conoce los nombres de esta práctica y no toca el stack gastos-hormiga.
¿Qué borra npm run limpiar-practica?
La Lambda, su log group, la tabla y los execution roles cuyo nombre empieza con gastos-hormiga-practica-funcion-role-. Espera a que DynamoDB termine de borrar la tabla, separa las policies del rol antes de borrarlo y sigue con lo demás si algún recurso ya no existe.
Parte 2: los mismos recursos como código (5 min)
Si un amigo quisiera lo mismo en su proyecto, tendrías que repetir cada clic y acordarte del nombre, el runtime, la llave id y el rol. Con infraestructura como código (IaC) esos pasos quedan escritos en un archivo, y AWS crea los recursos igual cada vez. Si el archivo trae un error, también lo repite, por eso revisamos qué va a cambiar antes de desplegar.
En este workshop usamos dos capas:
- CloudFormation recibe una plantilla y crea los recursos en el orden necesario.
- AWS CDK te deja escribir esa plantilla en TypeScript. Con
npm run synthves la plantilla que genera, sin desplegar nada.
Estas son las piezas del archivo:
- Un constructo (
new Table(...),new RestApi(...)): un recurso, o un grupo de recursos, que AWS debe crear. - El id (
'GastosTable'): el nombre del recurso dentro del archivo. En la tabla fijamos el logical ID conoverrideLogicalId, porque si cambia, CloudFormation cree que es otra tabla y borra la que tiene tus gastos. - Las propiedades (
tableName,runtime,memorySize): las decisiones que antes tomabas con clics. addMethod: conecta una ruta de API Gateway, comoPOST /gastos, con la Lambda que la atiende.addToRolePolicy: lo que puede hacer la Lambda. Sin permiso para escribir en DynamoDB, la función existe pero no puede guardar nada.CfnOutput: los valores que nos entrega al terminar, como la URL de la API.
Lee el stack
Abre infra/gastos-stack.ts en Kiro y sigue la numeración. También lo puedes ver aquí con número de línea.
La tabla. En la línea 32 empieza GastosTable, la tabla donde viven tus gastos. La línea 33 le da un nombre basado en el stack. Con ese nombre fijo, si un cambio necesitara reemplazar la tabla, CloudFormation bloquea el deploy antes de tocar la que ya tiene tus gastos (mientras no cambies ni quites tableName).
La línea 37 explica el removalPolicy: CDK guarda las tablas aunque borres el stack, y aquí pedimos lo contrario para que npm run limpiar no deje tus gastos huérfanos.
La API. En la línea 73 está la API. ¿Por qué REST API y no HTTP API, que es más nueva y barata? Porque las API keys solo existen en REST, y las usamos para proteger el POST.
La API key. En la línea 99 se crea la key que va a mandar tu Atajo. El usage plan de las líneas 104 a 110 limita a quien trae esa key: 2 peticiones por segundo y un máximo al día, para que nadie se acabe tus créditos con un loop.
Las Lambdas. La línea 44 es la receta de una Lambda: runtime, arquitectura, memoria, log group y cómo se empaqueta el TypeScript. La línea 65 registra, y la línea 94 dice que el POST /gastos necesita API key. La que lista empieza en la línea 69. Cada una apunta a un archivo de src/handlers/, que es donde falta el código.
Los permisos. Cada Lambda recibe solo lo que usa: la línea 66 permite escribir en la tabla y la línea 70 permite leerla, nada más.
Tip 🧠: Si algo te causa curiosidad, pregúntale a Kiro. 😉
Parte 3: construye la API de gastos (25 min)
Tus Lambdas están vacías
src/handlers/registrar.ts solo responde 501, que en HTTP significa "esto todavía no está implementado". src/handlers/listar.ts está igual.
Te toca: corre los tests
npm test tests/gastos.test.ts
Salen cinco en rojo. Cuatro son de las Lambdas vacías y los resuelve Kiro. El quinto, rechaza montos negativos, lo arreglas tú más adelante.
Pídele a Kiro la tarea 2
La tarea 2 de .kiro/specs/gastos-hormiga/tasks.md dice qué archivos tocar, cuáles no y qué tests deben pasar al final.
Te toca: pídele a Kiro la tarea 2
En tasks.md, da clic en Start task arriba de la tarea 2.
Si no ves el botón, escríbelo en el chat de Kiro:
Haz la tarea 2 de .kiro/specs/gastos-hormiga/tasks.md
Mientras trabaja, fíjate qué archivos abre: la spec y los helpers de src/shared/.
🤖 Cuando el código lo escribe una IA, tu trabajo es revisarlo.
Revisa lo que escribió
1. ¿Usa validarGasto()? Busca validarGasto( en registrar.ts. Si Kiro escribió su propia validación, pídele que use la de src/shared/validacion.ts, que es la que vas a arreglar tú.
2. ¿La lista usa QueryCommand? En listar.ts tiene que haber un QueryCommand, no un ScanCommand. Un Scan lee la tabla completa cada vez y DynamoDB cobra por lo que lee. Con tres gastos ni lo notas, con tres mil sí.
3. ¿Instaló algo? Mira package.json en el diff. La tarea no necesita paquetes nuevos, y las reglas de .kiro/steering/npm-seguro.md le piden a Kiro avisarte antes de agregar uno.
Entiende lo que hace el código
Que los tests pasen no basta. La IA escribe las líneas, pero si mañana le entregas esta app a tu equipo, quien responde por lo que hace eres tú. Antes de seguir, usa a Kiro para entender qué pasa cuando registras y consultas un gasto.
Te toca: pídele a Kiro una explicación visual
Explícame src/handlers/registrar.ts y listar.ts como si
nunca hubiera usado DynamoDB. No cambies archivos, no
despliegues ni instales nada.
Crea un HTML sencillo fuera de este repo y ábrelo. Muestra
dos flujos:
- POST /gastos: petición, validarGasto, PutCommand y tabla.
- GET /gastos: petición, QueryCommand y respuesta JSON.
En cada paso muestra el archivo, las líneas y un fragmento
corto. Explica pk, sk, PutCommand y QueryCommand, y por qué
Query no lee toda la tabla como Scan. Usa solo lo que exista
en mis archivos.
Mientras lo lees, sigue el recorrido: qué bloque recibe el gasto, cuál lo revisa, cuál lo guarda y cuál lo lee.
Compruébalo tú
Te toca: corre los tests desde tu terminal
npm test tests/gastos.test.ts
Solo queda en rojo rechaza montos negativos.
Sigue más de un test en rojo
Dile a Kiro: "el test X sigue en rojo, revísalo sin tocar validacion.ts". El test dice exactamente qué espera.
Prepara tu cuenta para CDK
CDK sube el código de tus Lambdas a un bucket de S3 antes de crearlas. Ese bucket y unos roles se crean una sola vez por cuenta y Región, y a eso se le llama bootstrap.
Te toca: haz el bootstrap, una sola vez
npm run bootstrap
Tarda un par de minutos. Al final dice que el entorno quedó listo.
Crea un stack aparte, CDKToolkit, con el bucket de S3, un repositorio de ECR que no vas a usar, roles de IAM y un parámetro de SSM. Se queda después del workshop: Qué se queda en tu cuenta explica cómo borrarlo.
Despliega
npm run diff compara tu código contra lo que hay en AWS. Como todavía no hay nada, lista todo como nuevo.
Te toca: mira el plan y despliega
npm run diff
npm run deploy
npm run deploy empaqueta tus Lambdas con esbuild, las sube al bucket y le pasa a CloudFormation la plantilla de CDK. Antes de crear nada muestra los cambios de permisos (IAM Statement Changes) y pregunta Do you wish to deploy these changes (y/n)?. Revisa que cada Lambda tenga una sola acción sobre la tabla y contesta y.
Error: Need to perform AWS calls ... Has the environment been bootstrapped?
Falta el bootstrap, o lo hiciste con otro perfil o en otra Región. Corre npm run bootstrap y repite el deploy.
Error: The config profile (gastos-hormiga) could not be found
Te falta iniciar sesión: aws login --profile gastos-hormiga (módulo 1).
Error: ... is not authorized to perform ... with an explicit deny in a service control policy
Casi siempre es la Región. Revisa que region en cdk.json sea la de tu proyecto (módulo 1).
Mientras se despliega
Te toca: mira cómo se crean los recursos
- Abre CloudFormation y entra al stack
gastos-hormiga. En Events ves cómo se crea cada recurso.
- Cuando termine, abre Resources. Hay 24 recursos. Tú escribiste la tabla, dos Lambdas con sus log groups, la API, la key y el plan. ¿Cuáles agregó CDK?
Los que agregó CDK
RegistrarGastoFunctionServiceRole...yListarGastosFunctionServiceRole...: un rol por Lambda, con permiso para escribir sus logs.- Las dos
...DefaultPolicy...: la policy de cada rol, con la acción de DynamoDB que escribiste conaddToRolePolicy. - Cuatro
...ApiPermission...: el permiso para que API Gateway invoque cada Lambda (dos por función: la ruta real y el botón Test de la consola). GastosApiDeployment...yGastosApiDeploymentStagev1...: una foto de tu API y el stagev1que le da URL.GastosApigastos...: la ruta/gastosy sus métodos, incluidos los dosOPTIONSdel CORS.GastosApiGastosUsagePlanUsagePlanKeyResource...: une tu API key con el usage plan.
Al terminar, npm run deploy imprime los Outputs: ApiUrl, ApiKeyId y TablaGastos.
Verifícalo tú
El script lee la URL y la key por ti, hace el POST y luego pide la lista con un GET. Funciona igual en Windows, macOS y Linux.
Te toca: registra tu primer gasto
npm run probar
Ahí está tu café, guardado por el código que escribió tu Kiro. Puedes verlo también en DynamoDB > Explore items, en la tabla gastos-hormiga-gastos.
Para registrar otro con tus datos (monto, descripción y categoría, en ese orden):
npm run probar 28 "galleta del súper" snacks
Opcional: encuentra tu API key y tu URL en la consola
Son los dos datos que va a usar tu Atajo en el módulo 5. No los copies todavía.
- Abre API Gateway > API keys y entra a
gastos-hormiga-key-1. Ahí está el valor de la key.
- Abre CloudFormation, entra al stack
gastos-hormigay, en Outputs, miraApiUrl.
¿Prefieres usar curl?
Saca la URL y la key con npm run datos mostrar-key, guárdalas en dos variables y manda la misma petición que hace el script.
Windows (PowerShell)
En PowerShell curl es otro comando, así que se escribe curl.exe. El JSON va en un archivo para evitar problemas con las comillas:
$API = "pega-aqui-tu-ApiUrl"
$KEY = "pega-aqui-tu-api-key"
'{"monto":45,"descripcion":"cafe","categoria":"cafes"}' |
Out-File -Encoding ascii gasto.json
curl.exe -X POST "$API/gastos" `
-H "x-api-key: $KEY" `
-H "x-origen: curl" `
-H "content-type: application/json" `
--data-binary "@gasto.json"
macOS y Linux
API="pega-aqui-tu-ApiUrl"
KEY="pega-aqui-tu-api-key"
curl -X POST "$API/gastos" \
-H "x-api-key: $KEY" \
-H "x-origen: curl" \
-H "content-type: application/json" \
-d '{"monto": 45, "descripcion": "café", "categoria": "cafes"}'
Si cierras la terminal, vuelve a guardar las variables antes de usar otro curl.
¿No quieres terminal? Usa Bruno
Bruno es una app para probar APIs desde una pantalla, sin pelearte con comillas. Manda la misma petición que el script.
Instala y abre Bruno. Crea una petición
POST.Copia el valor de
ApiUrldesde CloudFormation > Outputs, agrega/gastosal final y pégalo como URL.Agrega estos headers:
Header Valor x-api-keyEl valor de gastos-hormiga-key-1en API Gatewayx-origenbrunocontent-typeapplication/jsonEn el body JSON pega esto y envía la petición:
{ "monto": 45, "descripcion": "café de la esquina", "categoria": "cafes" }
Para ver los gastos, crea otra petición GET con la misma URL y envíala.
Responde {"error":"POST /gastos todavía no está implementado..."}
Desplegaste antes de aceptar el diff de Kiro. Acéptalo y vuelve a correr npm run deploy.
Tócale al código
Ahora te toca escribir a ti. Primero arranca el modo de desarrollo, para que guardar un archivo sea desplegarlo:
Te toca: arranca npm run sync y déjalo corriendo
npm run sync
Despliega una vez y se queda vigilando src/ e infra/. Cada vez que guardas, sube el cambio a tu Lambda en segundos, y en la misma terminal ves los logs de tus Lambdas en vivo.
⚠
npm run sync(cdk watch) es solo para desarrollo: salta partes de CloudFormation para ir más rápido. Para cambios que quieras conservar, regresa anpm run deploy.
Te toca: prueba un gasto negativo
En otra terminal (con el signo + del panel de la terminal de Kiro):
npm run probar negativo
Responde 201. Tu API acepta que te gastaste menos cincuenta pesos.
Abre src/shared/validacion.ts, que es lo que usa la Lambda de Kiro para validar el body. En la línea 19 se revisa que el monto sea número, pero nadie revisa que sea mayor a 0.
Te toca: escribe la validación a mano
Después de ese if, agrega la validación de que monto sea mayor a 0. Guarda, y mira en la otra terminal cómo npm run sync lo sube en segundos. Vuelve a correr el test y el script.
npm test tests/gastos.test.ts
npm run probar negativo
Si llevas 2 minutos atorado
Después del if que rechaza lo que no es número, agrega otra condición que regrese { ok: false, error: '...' } cuando monto <= 0. No necesitas tocar el handler.
Checkpoint ✅
Te toca: corre el checkpoint
npm run verify 4
Revisa que pasen los tests de gastos y los de infraestructura, que sin API key responda 403, que con key responda 201, que el gasto aparezca en el GET y que tu API ya rechace los montos negativos. Los gastos de prueba que crea los borra al terminar.
"Respondió 501: tu Lambda desplegada todavía es la versión vacía"
El código de Kiro está en tu laptop, pero no llegó a AWS. Revisa que npm run sync siga corriendo sin errores, o corre npm run deploy.
"Tu API todavía acepta montos negativos"
Tu arreglo está bien, pero no llegó a tu API. Revisa que npm run sync siga corriendo sin errores.
GET /gastos responde 403 justo después de desplegar
A veces API Gateway tarda unos segundos en publicar el stage. Espera medio minuto y repite.
Para saber más
El POST pide API key, ¿y el GET?
El GET /gastos está abierto: cualquiera con tu URL puede ver tus gastos. Lo vas a ver con tu propia página en el módulo 5. Por ahora quédate con la pregunta: ¿quién más tiene tu URL?
Si te sobra tiempo
- Agrega
transportea uno de tus gastos y mira cómo cambia elGET. - Haz que
descripcionno pueda tener solo números. Escribe primero el test.
Si te atoraste
Pega en Kiro el último error y el comando que lo produjo, y pídele que lo explique sin desplegar ni borrar nada (más en errores comunes). Si no te ayuda, levanta la mano. Para seguir con el módulo mientras tanto:
git stash -u
git switch modulo-4
npm ci
npm run deploy
git stash -u guarda tus cambios por si los quieres de vuelta. La rama modulo-4 tiene el módulo terminado: la página de Amplify, las Lambdas escritas y la validación arreglada. Después del workshop, git stash pop recupera lo que guardaste.