DEV Community

Cover image for AWS Lambda Durable Functions + IA: el combo que no sabías que necesitabas
Hazel Saenz for AWS

Posted on

AWS Lambda Durable Functions + IA: el combo que no sabías que necesitabas

Cómo instalar y usar el nuevo Kiro Power para Lambda Durable Functions paso a paso.


En el artículo anterior vimos que Lambda Durable Functions y Step Functions no compiten. Son dos filosofías diferentes para resolver el mismo problema: orquestación de workflows.

Step Functions orquesta visualmente. Durable Functions orquesta en código.

Y prometí que en este artículo íbamos a pasar de la teoría a la práctica.

Pero hay un detalle que no mencioné:

👉 Construir tu primera Durable Function no tiene que ser complicado.

Porque desde marzo de 2026, existe un Kiro Power que te guía paso a paso. Con IA. Dentro de tu IDE.

Y eso cambia todo.

Espera... ¿qué es Kiro?

Si es la primera vez que escuchas el nombre, no te preocupes.

Kiro es un IDE creado por AWS, potenciado con IA. Piensa en él como tu editor de código, pero con un agente de IA integrado que entiende lo que estás construyendo y te ayuda a hacerlo mejor.

Kiro IDE mostrando el agente de IA en acción

No es un chatbot que pega código de internet. No es un autocompletado glorificado.

Es un agente que entiende el contexto de tu proyecto, sigue buenas prácticas, y te guía mientras escribes código.

Puedes usarlo para escribir código, debuggear, hacer deploy, y mucho más. Si quieres conocerlo a fondo, te recomiendo esta guía de primeros pasos con Kiro. Pero lo que lo hace especial para este artículo es algo llamado Powers.

¿Qué es un Kiro Power?

Un Power es como darle superpoderes especializados al agente de IA de Kiro.

Sin un Power, el agente sabe de todo un poco. Con un Power instalado, el agente se convierte en experto en un tema específico.

Piensa en esto: si le pides a un asistente genérico que te ayude a construir una Durable Function, probablemente te dé una respuesta genérica. Puede que funcione. Puede que no.

Pero si ese asistente tiene cargado un Power de Lambda Durable Functions, se convierte en un experto que sabe las reglas, los patrones, y las trampas comunes. Y carga esa información dinámicamente según lo que estés haciendo.

El Lambda Durable Functions Power

El 5 de marzo de 2026, AWS lanzó el Kiro Power para Lambda Durable Functions.

Incluye guía especializada para todo el ciclo de desarrollo: desde el setup del proyecto y las reglas del replay model, pasando por steps, waits, callbacks, ejecución en paralelo, manejo de errores, testing local, hasta deploy con CDK, SAM, o CloudFormation.

Diagrama de lo que incluye el Power — replay model, steps, waits, error handling, testing, deployment

No tienes que leer toda la documentación antes de empezar. El Power te da lo que necesitas, cuando lo necesitas.

Instalando el Power

Esto es la parte más fácil. Literalmente un clic.

  1. Abre Kiro
  2. Ve al panel de Powers (en la barra lateral)
  3. En la sección Available, busca "Lambda Durable Functions"
  4. Haz clic en Install

Eso es todo.

No hay configuración adicional. No hay dependencias que instalar manualmente para el Power. Una vez instalado, el agente de Kiro ya tiene acceso a toda la guía especializada.

Nota: No todos los servicios de AWS tienen un Power dedicado. Los Powers son módulos especializados que se van agregando con el tiempo. Puedes explorar los disponibles en el panel de Powers de Kiro.

Ahora sí, vamos a construir algo.

Prerequisitos

Antes de escribir código, necesitas tener algunas herramientas instaladas. Los siguientes comandos te ayudan a verificar si ya las tienes. Si alguno falla, sigue el link de documentación correspondiente para instalarlo.

  1. AWS CLI instalado (versión 2.33.22 o superior) y configurado con tus credenciales:
aws --version
aws sts get-caller-identity
Enter fullscreen mode Exit fullscreen mode
  1. Node.js 22+ instalado (para este tutorial usaremos TypeScript):
node --version
Enter fullscreen mode Exit fullscreen mode
  1. AWS CDK instalado globalmente (versión 2.237.1 o superior). Este se instala a nivel global en tu máquina, no dentro del proyecto:
npm install -g aws-cdk
cdk --version
Enter fullscreen mode Exit fullscreen mode

Si prefieres Python, Lambda Durable Functions también soporta Python 3.11+. Pero para este tutorial vamos con TypeScript porque es el lenguaje que más uso y el que me resulta más natural para explicar.

Creando tu primer proyecto

Abre Kiro y crea una carpeta nueva para tu proyecto. Luego, en el chat del agente, puedes pedirle directamente:

"Crea un proyecto de Lambda Durable Function con TypeScript y CDK"

El agente, con el Power instalado, va a:

  1. Inicializar el proyecto con la estructura correcta
  2. Instalar las dependencias necesarias
  3. Configurar TypeScript
  4. Crear los archivos base

Pero para que entiendas qué está pasando, vamos a hacerlo paso a paso.

Inicializar el proyecto

mkdir image-processor
cd image-processor
npm init -y
Enter fullscreen mode Exit fullscreen mode

Instalar dependencias

npm install @aws/durable-execution-sdk-js
npm install --save-dev @aws/durable-execution-sdk-js-testing typescript @types/node
Enter fullscreen mode Exit fullscreen mode

Ese primer paquete es el SDK de Durable Execution. Es el core de las Durable Functions: lo que hace posible que tu función guarde checkpoints y sobreviva a fallos.

El segundo paquete (-testing) es para testear localmente sin necesidad de deployar a AWS.

Estructura del proyecto

Kiro va a generar esta estructura por ti cuando le pidas crear el proyecto. Pero si prefieres hacerlo manual, así es como debería verse:

image-processor/
├── src/
│   └── handler.ts          # Tu función durable
├── tests/
│   └── handler.test.ts     # Tests locales
├── infrastructure/
│   └── stack.ts             # CDK stack para deploy
├── tsconfig.json
└── package.json
Enter fullscreen mode Exit fullscreen mode

Simple. Sin carpetas innecesarias. Sin archivos que no vas a usar.

Tu primera Durable Function (generada por el Power)

Ahora viene la parte divertida.

¿Recuerdas el ejemplo del artículo anterior? Un usuario sube una foto y necesitas: validar que sea una imagen válida, redimensionar a diferentes tamaños, aplicar marca de agua, generar thumbnails, y guardar en S3.

Abre el chat de Kiro y escríbele algo como:

"Crea una Durable Function que procese imágenes: validar el archivo, redimensionar, aplicar marca de agua, generar thumbnails y guardar en S3"

El agente, con el Power instalado, genera el código por ti. No copia y pega de internet. Entiende las reglas del replay model, sabe qué va dentro de un step y qué no, y estructura el workflow siguiendo las buenas prácticas del SDK.

Esto es lo que el Power generó para mí cuando le pedí exactamente eso:

Screenshot del agente de Kiro generando el código de la Durable Function en el chat

...
export const handler = withDurableExecution(async (event: ImageEvent, context: DurableContext) => {
  const outputBucket = event.outputBucket ?? event.bucket;
  const outputPrefix = event.outputPrefix ?? 'processed';
  const baseName = event.key.replace(/\.[^.]+$/, '');
  const ext = event.key.match(/\.[^.]+$/)?.[0] ?? '.jpg';

  // Step 1: Validate the file
  const imageBuffer = await context.step('validate-file', async () => {
    const response = await s3.send(new GetObjectCommand({
      Bucket: event.bucket,
      Key: event.key,
    }));

    if (!response.Body) {
      throw new Error(`File not found: ${event.key}`);
    }

    const buffer = Buffer.from(await response.Body.transformToByteArray());
    const metadata = await sharp(buffer).metadata();

    if (!metadata.format || !['jpeg', 'png', 'webp', 'tiff'].includes(metadata.format)) {
      throw new Error(
        `Unsupported image format: ${metadata.format}. Supported: jpeg, png, webp, tiff`
      );
    }

    if (buffer.length > 50 * 1024 * 1024) {
      throw new Error('File exceeds 50MB limit');
    }

    context.logger.info('File validated', {
      format: metadata.format,
      width: metadata.width,
      height: metadata.height,
      size: buffer.length,
    });

    return buffer.toString('base64');
  });

  // Step 2: Resize the image
  const resizedBase64 = await context.step(
    'resize-image',
    async () => {
      const buffer = Buffer.from(imageBuffer, 'base64');
      const maxWidth = event.maxWidth ?? 1920;
      const maxHeight = event.maxHeight ?? 1080;

      const resized = await sharp(buffer)
        .resize(maxWidth, maxHeight, { fit: 'inside', withoutEnlargement: true })
        .toBuffer();

      const resizedKey = `${outputPrefix}/${baseName}-resized${ext}`;
      await s3.send(new PutObjectCommand({
        Bucket: outputBucket,
        Key: resizedKey,
        Body: resized,
        ContentType: `image/${ext.replace('.', '')}`,
      }));

      context.logger.info('Image resized', { key: resizedKey });
      return resized.toString('base64');
    },
    { retryStrategy }
  );
...
Enter fullscreen mode Exit fullscreen mode

Ver codigo completo: https://github.com/hsaenzG/durable-funtions-demo/blob/main/src/handler.ts

Parece código normal, ¿verdad? Y lo es. Pero con superpoderes.

Qué está pasando aquí

Cada context.step() es un checkpoint. Si tu función falla aplicando la marca de agua (paso 3), cuando se reintente, no va a volver a validar ni redimensionar. Salta directamente al paso 3 con los resultados que ya tenía guardados.

Eso es el replay model en acción. Y para un workflow de procesamiento de imágenes, donde cada paso puede tardar segundos o minutos, no repetir trabajo es la diferencia entre una buena experiencia y una pesadilla.

Fíjate también en context.logger en lugar de console.log: es replay-aware. No duplica logs cuando la función se reintenta.

El Kiro Power y la regla de oro del Replay Model

Aquí es donde el Kiro Power realmente brilla. Porque hay una regla que la mayoría no conocemos hasta que algo se rompe:

Todo código no determinístico DEBE ir dentro de un step.

Y el Power te avisa cuando estás a punto de violar esa regla. No tienes que memorizar todos los casos. El agente los detecta y te sugiere la corrección antes de que sea un problema.

creenshot del agente de Kiro detectando una violación del replay model y sugiriendo la corrección

Pero, ¿por qué importa tanto esta regla?

Cuando una Durable Function se reintenta (replay), ejecuta todo el código desde el principio. Los steps que ya completaron se saltan (usa el resultado guardado). Pero el código que está fuera de los steps se ejecuta de nuevo.

Si ese código da un resultado diferente en el replay... las cosas se rompen.

¿Qué es código no determinístico? Cualquier cosa que puede dar un resultado diferente cada vez:

  • Date.now() → diferente cada vez
  • Math.random() → diferente cada vez
  • Llamadas a APIs externas → pueden devolver datos diferentes
  • Consultas a bases de datos → los datos pueden haber cambiado
  • Generación de UUIDs → diferente cada vez

Veamos un ejemplo concreto:

// ❌ MAL: Date.now() fuera de un step
const timestamp = Date.now(); // Diferente en cada replay
await context.step('save', async () => save(timestamp));

// ✅ BIEN: Date.now() dentro del step
const result = await context.step('save-with-timestamp', async () => {
  const timestamp = Date.now(); // Se guarda con el checkpoint
  return save(timestamp);
});
Enter fullscreen mode Exit fullscreen mode

Sin el Power, descubrir este tipo de errores es prueba y error. Con el Power, el agente te lo señala mientras escribes.

Además, puedes instalar el plugin de ESLint para Durable Functions que atrapa estos errores en tiempo de desarrollo:

npm install --save-dev @aws/durable-execution-sdk-js-eslint-plugin
Enter fullscreen mode Exit fullscreen mode

Testeando localmente

No necesitas hacer deploy para probar tu función. El SDK incluye un test runner local.

import { LocalDurableTestRunner } from '@aws/durable-execution-sdk-js-testing';
import { handler } from '../src/handler';

describe('Image Processing Workflow', () => {
  it('should process an image successfully', async () => {
    const runner = new LocalDurableTestRunner(handler, { skipTime: true });

    const result = await runner.run({
      input: {
        bucket: 'my-images-bucket',
        key: 'uploads/photo.jpg',
        userId: 'user-123'
      }
    });

    // Verificar que el procesamiento se completó
    expect(result.status).toBe('completed');
    expect(result.originalKey).toBe('uploads/photo.jpg');

    // Verificar que los steps se ejecutaron
    const steps = runner.getOperations();
    const validateStep = steps.find(op => op.name === 'validate-image');
    const resizeStep = steps.find(op => op.name === 'resize-image');
    const watermarkStep = steps.find(op => op.name === 'apply-watermark');
    const thumbnailStep = steps.find(op => op.name === 'generate-thumbnails');
    const saveStep = steps.find(op => op.name === 'save-to-s3');

    expect(validateStep).toBeDefined();
    expect(resizeStep).toBeDefined();
    expect(watermarkStep).toBeDefined();
    expect(thumbnailStep).toBeDefined();
    expect(saveStep).toBeDefined();
  });
});
Enter fullscreen mode Exit fullscreen mode

El skipTime: true es clave: hace que los wait se salten instantáneamente en los tests.

Y fíjate que buscamos los steps por nombre, no por índice. Eso es una buena práctica que el Power te recuerda constantemente: siempre nombra tus steps de forma descriptiva.

Haciendo deploy con CDK

Ya tienes tu función funcionando localmente. Ahora toca subirla a AWS.

Para esto usamos AWS CDK, que te permite definir tu infraestructura en código (TypeScript, en nuestro caso).

import * as cdk from 'aws-cdk-lib';
import * as lambda from 'aws-cdk-lib/aws-lambda';
import * as logs from 'aws-cdk-lib/aws-logs';
import * as iam from 'aws-cdk-lib/aws-iam';

export class DurableFunctionStack extends cdk.Stack {
  constructor(scope: cdk.App, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    // Log group explícito para mejor control
    const logGroup = new logs.LogGroup(this, 'ImageProcessingLogs', {
      logGroupName: '/aws/lambda/imageProcessing',
      retention: logs.RetentionDays.ONE_WEEK,
      removalPolicy: cdk.RemovalPolicy.DESTROY,
    });

    // La función durable
    const imageFunction = new lambda.Function(this, 'ImageProcessing', {
      runtime: lambda.Runtime.NODEJS_24_X,
      handler: 'handler.handler',
      code: lambda.Code.fromAsset('dist'),
      logGroup: logGroup,
      durableConfig: {
        executionTimeout: cdk.Duration.hours(1),
        retentionPeriod: cdk.Duration.days(7),
      },
    });

    // Política de permisos para durable execution
    imageFunction.role?.addManagedPolicy(
      iam.ManagedPolicy.fromAwsManagedPolicyName(
        'service-role/AWSLambdaBasicDurableExecutionRolePolicy'
      )
    );

    // Crear alias para invocación
    const version = imageFunction.currentVersion;
    const alias = new lambda.Alias(this, 'ProdAlias', {
      aliasName: 'prod',
      version: version,
    });

    new cdk.CfnOutput(this, 'FunctionAliasArn', {
      value: alias.functionArn,
    });
  }
}
Enter fullscreen mode Exit fullscreen mode

Lo que importa aquí

Hay tres cosas que no puedes olvidar al hacer deploy de una Durable Function:

  1. durableConfig: Sin esto, tu función es una Lambda normal. El executionTimeout define cuánto tiempo máximo puede correr tu workflow (hasta 1 año). El retentionPeriod define cuánto tiempo se guardan los datos de ejecución.

  2. AWSLambdaBasicDurableExecutionRolePolicy: Esta política le da a tu función permisos para hacer checkpoints (lambda:CheckpointDurableExecution) y leer el estado de ejecución (lambda:GetDurableExecutionState). Sin ella, los steps no pueden guardar su progreso.

  3. ARN calificado: Las Durable Functions requieren un ARN calificado para ser invocadas. Un ARN (Amazon Resource Name) es el identificador único de un recurso en AWS. "Calificado" significa que incluye la versión o alias de la función. No puedes invocar una Durable Function con el ARN sin calificar.

# ✅ Correcto: con alias
aws lambda invoke --function-name 'MiStack-MiFuncion-abc123:prod' response.json

# ✅ Correcto: con versión
aws lambda invoke --function-name 'MiStack-MiFuncion-abc123:1' response.json

# ❌ Incorrecto: sin calificar — va a fallar
aws lambda invoke --function-name MiStack-MiFuncion-abc123 response.json
Enter fullscreen mode Exit fullscreen mode

Nota: CDK genera nombres de recursos con un sufijo aleatorio (ej: ImageProcessorStack-ImageProcessor5D0B0257-lBARjqDaXdWK). Puedes encontrar el nombre exacto de tu función en el output del cdk deploy o en la consola de AWS Lambda.

Deploy

# Compilar TypeScript
npx tsc

# Deploy con CDK
cdk deploy
Enter fullscreen mode Exit fullscreen mode

CDK se encarga de crear todo: la función, el rol de IAM, los permisos, el log group, la versión y el alias.

IScreenshot del deploy con CDK ejecutándose en la terminal

Invocando tu función

Ya está en la nube. Ahora vamos a probarla.

Primero, sube una imagen de prueba al bucket de S3 que CDK creó:

aws s3 cp tu-imagen.jpg \
  s3://TU-BUCKET-S3/photos/test.jpg \
  --profile TU-PERFIL \
  --region us-east-1
Enter fullscreen mode Exit fullscreen mode

Nota: El nombre del bucket lo genera CDK automáticamente (ej: imageprocessorstack-imagebucket97210811-x5j3e61lysxh). Puedes encontrarlo en el output del cdk deploy o en la consola de S3.

Ahora sí, invoca la función:

aws lambda invoke \
  --function-name 'TU-NOMBRE-DE-FUNCION:prod' \
  --invocation-type Event \
  --durable-execution-name "test-$(date +%s)" \
  --payload '{"bucket":"TU-BUCKET-S3","key":"photos/test.jpg"}' \
  --cli-binary-format raw-in-base64-out \
  --profile TU-PERFIL \
  --region us-east-1 \
  response.json

cat response.json
Enter fullscreen mode Exit fullscreen mode

Tip: Reemplaza TU-NOMBRE-DE-FUNCION, TU-BUCKET-S3 y TU-PERFIL con los valores reales de tu entorno. Revisa los outputs del cdk deploy para encontrarlos.

Screenshot de la respuesta JSON en la terminal después de invocar la función

Algunos detalles sobre estos parámetros:

  • --invocation-type Event: Invoca la función de forma asíncrona. Para workflows largos como procesamiento de imágenes, esto es lo recomendado. Si quieres esperar la respuesta, usa RequestResponse.
  • --durable-execution-name "test-$(date +%s)": Genera un nombre único por ejecución usando un timestamp. Esto garantiza idempotencia: si invocas con el mismo nombre dos veces, la segunda no crea una ejecución nueva.
  • --profile y --region: Si tienes múltiples perfiles de AWS configurados, no olvides especificarlos.

Monitoreando tus ejecuciones

Ya invocaste tu función. ¿Y ahora? ¿Cómo sabes si funcionó?

No estás a ciegas. La consola de AWS Lambda tiene una interfaz dedicada para Durable Functions donde puedes ver todo lo que está pasando.

Ve a tu función en la consola y busca la pestaña Durable executions. Ahí vas a encontrar la lista de todas las ejecuciones con su estado: running, completed, failed.

Screenshot de la consola de AWS Lambda mostrando la lista de ejecuciones durables con sus estados

Haz clic en cualquier ejecución y vas a ver el detalle completo: qué steps se completaron, cuáles fallaron, cuánto tardó cada uno, y los datos que se guardaron en cada checkpoint.

Screenshot del detalle de una ejecución durable mostrando los steps completados y sus resultados

Esto es oro para debuggear. Si algo falló en el paso 3, puedes ver exactamente qué datos tenía en ese momento y qué error se produjo. Sin adivinar. Sin buscar en CloudWatch a ciegas.

Por qué esto importa si estás empezando

Sin el Power, el camino típico es: leer toda la documentación, tropezar con el replay model, configurar el proyecto a mano, y descubrir errores de deploy por prueba y error.

Con el Power, el agente te guía en cada paso. No elimina la necesidad de entender los conceptos (eso sigue siendo importante). Pero reduce drásticamente el tiempo entre "quiero aprender Durable Functions" y "tengo una funcionando en producción".

Limpieza de recursos

Si solo estás probando, recuerda eliminar los recursos para evitar cargos:

cdk destroy
Enter fullscreen mode Exit fullscreen mode

Esto elimina la función Lambda, el rol de IAM, el log group, y todos los recursos asociados. CDK se encarga de todo.

Lo que aprendiste

Instalaste un Kiro Power con un clic, creaste un proyecto desde cero, dejaste que el agente generara tu primera Durable Function con el mismo ejemplo de procesamiento de imágenes del artículo anterior, entendiste la regla de oro del replay model, testeaste localmente sin deploy, y subiste todo a AWS con CDK.

De cero a producción. Con IA guiándote en cada paso.

Qué sigue

Ya tienes el workflow de procesamiento de imágenes funcionando. Pero lo construimos en el camino feliz: todo sale bien, nada falla.

En el próximo artículo vamos a romper cosas a propósito. Vamos a ver cómo los checkpoints automáticos mantienen tu progreso incluso cuando algo truena, y cómo el replay model recupera la ejecución exactamente donde quedó.

Código real. Fallos reales. Recuperación real.


¿Te resultó útil este artículo? Compártelo con tu equipo o déjame saber en los comentarios qué otros temas de serverless te gustaría que cubriera. Y si ya instalaste el Kiro Power y estás construyendo tu primera Durable Function, me encantaría escuchar tu experiencia.

Top comments (0)