
De localhost a la nube: construye tu primera API Serverless con FastAPI, Docker y AWS Lambda
El objetivo del manual FastAPI + Docker + AWS Lambda es dar a conocer un conjunto de herramientas que permiten a cualquier persona con conocimientos básicos de desarrollo publicar una API en Internet de manera rápida, económica y escalable. Además, el uso de Docker facilita la creación de soluciones agnósticas de la nube, al empaquetar la aplicación junto con sus dependencias en un entorno reproducible. Tiempo estimado: 100 minutos Nivel: Básico–intermedio
Introducción: ¡En mi computadora sí funciona!
Imagina que acabas de desarrollar una API para uno de tus clientes.
Después de varias horas programando, finalmente consigues que todo funcione. Ejecutas tu aplicación, realizas algunas pruebas y obtienes exactamente los resultados que esperabas.
Tu API está disponible en:
localhost:8000Presentas el resultado a tu cliente, quien parece bastante satisfecho.
Pero entonces llega una pregunta:
—¡Excelente! ¿Cuál es la dirección para que nuestros usuarios puedan comenzar a utilizarla?
Y es en ese momento cuando descubres que desarrollar una aplicación es solamente una parte del trabajo.
Ahora necesitas responder otras preguntas:
- ¿Dónde vamos a publicar nuestra API?
- ¿Necesitamos contratar un servidor?
- ¿Cómo instalamos las dependencias?
- ¿Qué ocurre si tenemos miles de solicitudes?
- ¿Quién va a administrar la infraestructura?
Tradicionalmente, una de las soluciones habría sido contratar un servidor, instalar Python, configurar nuestra aplicación y mantener todo funcionando.
Pero ¿qué tal si pudiéramos ejecutar nuestra aplicación en la nube sin tener que administrar directamente un servidor?
Bienvenido al mundo del serverless computing.
En este workshop vamos a construir una API desde cero, empaquetarla utilizando Docker y desplegarla en AWS Lambda.
Al finalizar tendremos una URL pública capaz de recibir solicitudes desde Internet.
Y entenderemos cómo llegamos hasta ahí.
1. ¿Qué vamos a construir?
Nuestro objetivo es desarrollar una sencilla API REST que permita recibir solicitudes HTTP y devolver respuestas JSON.
Por ejemplo, al realizar la siguiente solicitud:
1
GET /hello/CarlosObtendremos:
1
2
3
{
"message": "Hello, Carlos!"
}Además, agregaremos un endpoint POST que recibirá información en formato JSON.
Pero lo interesante no será solamente desarrollar esos endpoints, sino construir la infraestructura necesaria para publicarlos.
Nuestra arquitectura final será:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
USUARIO
|
v
INTERNET
|
v
API GATEWAY
|
v
AWS LAMBDA
|
v
MANGUM
|
v
FASTAPI
Imagen de contenedor:
FastAPI + Mangum + Python
|
v
AMAZON ECR
|
v
AWS LAMBDACada tecnología resolverá un problema específico.
2. ¿Qué herramientas necesitamos?
Python
Nuestro lenguaje de programación principal.
FastAPI
Un framework moderno de Python que permite construir APIs con validación de datos y documentación interactiva automática.
Docker
Nos permitirá empaquetar nuestra aplicación y sus dependencias para ejecutarla dentro de un entorno reproducible.
Mangum
Es un adaptador entre aplicaciones ASGI, como FastAPI, y eventos de AWS Lambda.
Amazon Elastic Container Registry (ECR)
Almacenará la imagen Docker que utilizaremos para desplegar nuestra aplicación.
AWS Lambda
Ejecutará nuestra aplicación bajo un modelo serverless.
Amazon API Gateway
Expondrá nuestra función Lambda mediante una URL HTTP pública.
AWS CLI
Permitirá administrar los recursos de AWS utilizando comandos desde la terminal.
3. Preparación del entorno
Antes de comenzar necesitamos:
- Python 3.12
- Docker con el complemento Buildx
- Una cuenta de AWS
- AWS CLI versión 2
- Un editor como Visual Studio Code
- Conocimientos básicos de Python y terminal
Para este tutorial utilizaremos comandos compatibles con Bash, disponible en Linux, macOS o WSL en Windows.
Primero verificamos que tenemos las herramientas necesarias.
1
2
3
4
python3 --version
docker --version
docker buildx version
aws --versionTambién debemos configurar el acceso a AWS CLI. Es recomendable utilizar credenciales temporales mediante IAM Identity Center o un perfil autorizado, y nunca utilizar las credenciales de la cuenta raíz.
Verificamos nuestra sesión:
1
aws sts get-caller-identitySi está configurada correctamente, obtendremos información sobre nuestra identidad de AWS.
Importante: necesitaremos permisos para crear recursos en IAM, ECR, Lambda, API Gateway y CloudWatch Logs.
Parte 1. Construyendo nuestra API con FastAPI
4. Crear el proyecto
Comencemos creando una carpeta para nuestro proyecto.
1
2
3
mkdir fastapi-lambda-workshop
cd fastapi-lambda-workshopCreamos un entorno virtual:
1
python3 -m venv .venvLo activamos:
1
source .venv/bin/activateEn Windows PowerShell podemos utilizar:
1
.venv\Scripts\Activate.ps1Ahora creamos nuestra carpeta de aplicación:
1
2
3
4
mkdir app
touch app/__init__.py
touch app/main.py
touch requirements.txtNuestra estructura inicial será:
1
2
3
4
5
6
7
fastapi-lambda-workshop/
│
├── app/
│ ├── __init__.py
│ └── main.py
│
└── requirements.txt5. Instalar nuestras dependencias
En el archivo
requirements.txt agregamos:1
2
3
=0.115,<1.0
=0.30,<1.0
=0.19,<1.0Instalamos las dependencias:
1
pip install -r requirements.txt¿Qué hace cada una?
FastAPI permite construir nuestra API. Uvicorn es el servidor ASGI que utilizaremos para ejecutarla localmente. Mangum permitirá adaptar posteriormente nuestra aplicación a Lambda.
6. Crear nuestra primera API
Abrimos
app/main.py y agregamos:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
from fastapi import FastAPI
from mangum import Mangum
from pydantic import BaseModel, Field
app = FastAPI(
title="API del workshop FastAPI + AWS Lambda",
description="Ejemplo práctico: de localhost a la nube.",
version="1.0.0"
)
class GreetingRequest(BaseModel):
name: str = Field(min_length=1, max_length=80)
def root():
return {
"message": "Hello from FastAPI!"
}
def health():
return {
"status": "ok"
}
def hello(name: str):
return {
"message": f"Hello, {name}!"
}
def create_greeting(payload: GreetingRequest):
return {
"message": f"Hola, {payload.name}!"
}
handler = Mangum(app, lifespan="off")¿Qué acabamos de construir?
Nuestra aplicación tiene cuatro endpoints:
| Método | Endpoint | Función |
|---|---|---|
| GET | / | Mensaje de bienvenida |
| GET | /health | Estado de la API |
| GET | /hello/{name} | Saludo personalizado |
| POST | /greetings | Recibe datos JSON y genera un saludo |
También hemos utilizado Pydantic para definir y validar los datos recibidos.
Por ejemplo:
1
2
class GreetingRequest(BaseModel):
name: str = Field(min_length=1, max_length=80)Esto significa que nuestro endpoint POST espera un objeto con un campo
name que contenga entre 1 y 80 caracteres.La última instrucción:
1
handler = Mangum(app, lifespan="off")será especialmente importante cuando llevemos nuestra aplicación a AWS Lambda.
Por ahora podemos dejarla ahí.
7. Ejecutar FastAPI localmente
Ejecutamos:
1
uvicorn app.main:app --reloadSi todo está configurado correctamente, nuestra aplicación estará disponible en:
127.0.0.1:8000Ahora abrimos:
127.0.0.1:8000/docs<text><escape> </escape></text>
Aquí descubrimos una de las características más interesantes de FastAPI: la documentación interactiva de nuestra API se genera automáticamente.
Desde esta interfaz podemos explorar y probar los endpoints sin construir una aplicación frontend.
8. Probar los endpoints
Desde una segunda terminal ejecutamos:
1
curl http://127.0.0.1:8000/Resultado:
1
2
3
{
"message": "Hello from FastAPI!"
}Ahora:
1
curl http://127.0.0.1:8000/hello/CarlosResultado:
1
2
3
{
"message": "Hello, Carlos!"
}Probemos también nuestro endpoint POST:
1
2
3
4
curl -X POST \
http://127.0.0.1:8000/greetings \
-H "Content-Type: application/json" \
-d '{"name":"Ana"}'Resultado esperado:
1
2
3
{
"message": "Hola, Ana!"
}¡Excelente!
Nuestra API está funcionando.
Pero seguimos teniendo el mismo problema del principio.
Solo podemos acceder a ella desde nuestra computadora.
Es momento de dar el siguiente paso.
Parte 2. Dockerizando nuestra aplicación
9. ¿Por qué necesitamos Docker?
Imagina que queremos ejecutar nuestra aplicación en una computadora diferente.
Necesitaríamos instalar Python, las librerías correspondientes y preparar el entorno.
Además, ¿qué sucede si esa computadora tiene una versión distinta de Python?
Podríamos encontrarnos con el famoso problema:
“Pero en mi computadora sí funciona...”
Docker nos ayuda a reducir estos problemas al empaquetar la aplicación y sus dependencias en una imagen.
Esa imagen podrá ejecutarse en cualquier entorno compatible con el contenedor.
10. Crear nuestro primer Dockerfile
En la raíz del proyecto creamos:
Dockerfile.localAgregamos:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
FROM python:3.12-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY app ./app
EXPOSE 8000
CMD [
"uvicorn",
"app.main:app",
"--host",
"0.0.0.0",
"--port",
"8000"
]Nota: la instrucción
CMD anterior puede escribirse en una sola línea si lo prefiere tu editor de Dockerfiles:1
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]¿Qué significa cada instrucción?
FROM: define la imagen base que utilizaremos.
WORKDIR: establece nuestro directorio de trabajo.
COPY: copia archivos de nuestro proyecto hacia la imagen.
RUN: ejecuta comandos durante la construcción de la imagen.
EXPOSE: documenta el puerto utilizado por nuestra aplicación.
CMD: indica qué comando se ejecutará al iniciar el contenedor.
También crearemos
.dockerignore:1
2
3
4
5
6
7
8
.git
.venv
venv
__pycache__
*.pyc
*.pyo
.env
.pytest_cacheEsto evita incluir archivos innecesarios o sensibles dentro del contexto de construcción.
11. Construir nuestra imagen
Ejecutamos:
1
2
3
docker build \
-f Dockerfile.local \
-t fastapi-workshop:local .Docker leerá nuestro Dockerfile, descargará la imagen base cuando sea necesario, instalará las dependencias y preparará nuestra aplicación.
Al finalizar tendremos una imagen llamada:
fastapi-workshop:localPodemos verificarla:
1
docker images12. Ejecutar nuestra API dentro de Docker
Primero detenemos Uvicorn si continúa ejecutándose, para liberar el puerto 8000.
Después:
1
2
3
docker run --rm \
-p 8000:8000 \
fastapi-workshop:localEl parámetro
-p 8000:8000 conecta el puerto 8000 de nuestra computadora con el puerto 8000 del contenedor.Ahora abrimos nuevamente:
localhost:8000/docs¡Nuestra API continúa funcionando!
Pero esta vez está ejecutándose dentro de un contenedor Docker.
Hemos conseguido que nuestra aplicación tenga un entorno de ejecución reproducible.
Sin embargo, Docker por sí solo no publica nuestra aplicación en Internet.
Necesitamos desplegarla en algún lugar.
Parte 3. Preparar FastAPI para AWS Lambda
13. ¿Qué es AWS Lambda?
AWS Lambda es un servicio de cómputo serverless que permite ejecutar código en respuesta a eventos.
A diferencia de un servidor tradicional, no necesitamos administrar directamente una máquina virtual para mantener nuestra función funcionando.
En nuestro caso, las solicitudes llegarán mediante API Gateway y se convertirán en eventos que serán enviados a Lambda.
Pero tenemos un pequeño desafío.
FastAPI es una aplicación ASGI, mientras que Lambda ejecuta funciones a través de un mecanismo de invocación basado en eventos.
¿Cómo conectamos estos dos modelos?
14. La función de Mangum
Aquí entra Mangum.
Mangum funciona como un adaptador que interpreta los eventos de Lambda y permite que nuestra aplicación FastAPI los procese como solicitudes ASGI.
El flujo será:
1
2
3
4
5
6
7
8
9
10
11
12
13
API Gateway
|
v
AWS Lambda
|
v
Mangum
|
v
FastAPI
|
v
Respuesta JSONYa agregamos Mangum en nuestro código:
1
handler = Mangum(app, lifespan="off")Este objeto será el punto de entrada de nuestra función Lambda.
La opción
lifespan="off" desactiva los eventos de inicio y cierre de ASGI, algo adecuado para nuestro ejemplo, que no necesita inicialización especial.15. Crear el Dockerfile para Lambda
Ahora necesitamos construir una imagen compatible con el entorno de AWS Lambda.
No utilizaremos exactamente la misma imagen Docker con la que ejecutamos Uvicorn.
AWS proporciona imágenes base específicas para Lambda que incluyen componentes necesarios para comunicarse con su runtime.
Crearemos un archivo:
Dockerfile.lambdaCon el siguiente contenido:
1
2
3
4
5
6
7
8
9
10
11
FROM public.ecr.aws/lambda/python:3.12
COPY requirements.txt ${LAMBDA_TASK_ROOT}/
RUN pip install --no-cache-dir \
-r ${LAMBDA_TASK_ROOT}/requirements.txt \
--target ${LAMBDA_TASK_ROOT}
COPY app ${LAMBDA_TASK_ROOT}/app
CMD ["app.main.handler"]¿Qué cambió?
Ahora utilizamos:
1
FROM public.ecr.aws/lambda/python:3.12Esta imagen está preparada para el runtime de Python en AWS Lambda.
También cambiamos nuestro comando final.
Antes ejecutábamos Uvicorn:
1
uvicorn app.main:appAhora indicamos:
1
CMD ["app.main.handler"]Esto significa que Lambda buscará el objeto
handler dentro del módulo app.main.Recuerda que este objeto lo creamos utilizando Mangum.
Parte 4. Construir y probar nuestra imagen de Lambda
16. Construir la imagen
Ejecutamos:
1
2
3
4
5
6
docker buildx build \
--platform linux/amd64 \
--provenance=false \
--load \
-f Dockerfile.lambda \
-t fastapi-workshop:lambda .Aquí estamos utilizando algunos parámetros importantes:
--platform linux/amd64: construye para la arquitectura x86-64 que utilizaremos en Lambda.
--provenance=false: evita generar metadatos de procedencia que pueden producir formatos de imagen incompatibles con Lambda.
--load: carga la imagen construida en nuestro Docker local.
17. Probar Lambda localmente
Una ventaja de las imágenes base de AWS Lambda es que incluyen un emulador de su interfaz de ejecución.
Esto nos permite realizar pruebas antes de desplegar nuestra aplicación en AWS.
Ejecutamos:
1
2
3
4
docker run --rm \
--platform linux/amd64 \
-p 9000:8080 \
fastapi-workshop:lambdaAhora nuestra imagen está escuchando invocaciones simuladas de Lambda a través del puerto 9000.
Pero hay una diferencia importante:
No podemos realizar una solicitud GET directamente al puerto 9000 como si fuera Uvicorn.
Tenemos que enviar un evento con una estructura similar a la utilizada por API Gateway.
18. Crear un evento de prueba
En la raíz del proyecto creamos:
event.jsonAgregamos:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
{
"version": "2.0",
"routeKey": "$default",
"rawPath": "/hello/Carlos",
"rawQueryString": "",
"headers": {
"accept": "*/*",
"host": "localhost"
},
"requestContext": {
"accountId": "123456789012",
"apiId": "local-demo",
"domainName": "localhost",
"http": {
"method": "GET",
"path": "/hello/Carlos",
"protocol": "HTTP/1.1",
"sourceIp": "127.0.0.1",
"userAgent": "curl"
},
"requestId": "local-request",
"routeKey": "$default",
"stage": "$default",
"timeEpoch": 0
},
"isBase64Encoded": false
}Este archivo simula el evento que recibiría nuestra función desde una API HTTP de API Gateway.
19. Invocar nuestra función localmente
Con el contenedor en ejecución, abrimos otra terminal y ejecutamos desde la carpeta del proyecto:
1
2
3
4
curl -X POST \
http://localhost:9000/2015-03-31/functions/function/invocations \
-H "Content-Type: application/json" \
--data-binary .jsonDeberíamos obtener una respuesta de Lambda similar a:
1
2
3
4
5
6
7
8
{
"statusCode": 200,
"headers": {
"content-type": "application/json"
},
"body": "{\"message\":\"Hello, Carlos!\"}",
"isBase64Encoded": false
}El contenido exacto de los encabezados puede variar.
Lo importante es observar que la función procesa nuestro evento y devuelve una respuesta HTTP válida.
¡Nuestra aplicación ya está preparada para ejecutarse en Lambda!
Solo falta publicarla.
Parte 5. Desplegar nuestra aplicación en AWS
20. Configurar nuestras variables
Para simplificar los siguientes comandos utilizaremos algunas variables de entorno en Bash.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
export REGION=us-east-1
export PROJECT=fastapi-workshop
export ACCOUNT_ID=$(
aws sts get-caller-identity \
--query Account \
--output text
)
export REPOSITORY_URI="${ACCOUNT_ID}.dkr.ecr.${REGION}.amazonaws.com/${PROJECT}"
export IMAGE_URI="${REPOSITORY_URI}:latest"
export ROLE_NAME="${PROJECT}-role"
export FUNCTION_ARN="arn:aws:lambda:${REGION}:${ACCOUNT_ID}:function:${PROJECT}"Utilizaremos la región
us-east-1 como ejemplo.Puedes modificarla por otra región de AWS compatible con los servicios utilizados.
El repositorio ECR y la función Lambda deberán encontrarse en la misma región.
21. Crear un repositorio en Amazon ECR
Antes de crear nuestra función necesitamos almacenar la imagen Docker en Amazon Elastic Container Registry.
Ejecutamos:
1
2
3
aws ecr create-repository \
--repository-name "$PROJECT" \
--region "$REGION"Esto creará un repositorio privado llamado:
fastapi-workshop22. Autenticar Docker con Amazon ECR
Ejecutamos:
1
2
3
4
5
aws ecr get-login-password \
--region "$REGION" \
| docker login \
--username AWS \
--password-stdin "${ACCOUNT_ID}.dkr.ecr.${REGION}.amazonaws.com"Con esto Docker podrá enviar imágenes a nuestro registro de ECR.
23. Etiquetar y subir nuestra imagen
Primero asignamos a nuestra imagen local la dirección del repositorio:
1
2
3
docker tag \
fastapi-workshop:lambda \
"$IMAGE_URI"Después la publicamos:
1
docker push "$IMAGE_URI"Al finalizar, nuestra imagen estará disponible en Amazon ECR.
Podemos comprobarlo utilizando:
1
2
3
aws ecr describe-images \
--repository-name "$PROJECT" \
--region "$REGION"Ahora tenemos nuestra aplicación empaquetada y almacenada en AWS.
Pero todavía no se está ejecutando.
24. Crear un rol IAM para nuestra función
AWS Lambda necesita un rol de ejecución con permisos para realizar determinadas operaciones.
En nuestro ejemplo, solamente necesita permisos básicos para escribir registros en CloudWatch Logs.
Crearemos:
iam-trust-policy.jsonCon este contenido:
1
2
3
4
5
6
7
8
9
10
11
12
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "lambda.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
}Este documento indica que el servicio Lambda puede asumir el rol.
Ahora creamos el rol:
1
2
3
aws iam create-role \
--role-name "$ROLE_NAME" \
--assume-role-policy-document file://iam-trust-policy.jsonAsignamos la política administrada que permite escribir registros:
1
2
3
aws iam attach-role-policy \
--role-name "$ROLE_NAME" \
--policy-arn arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRolePara este ejercicio no es necesario otorgar a nuestra función permisos generales de administración de AWS.
25. Crear nuestra función Lambda
Finalmente vamos a crear la función utilizando nuestra imagen Docker.
1
2
3
4
5
6
7
8
9
aws lambda create-function \
--function-name "$PROJECT" \
--package-type Image \
--code "ImageUri=${IMAGE_URI}" \
--role "arn:aws:iam::${ACCOUNT_ID}:role/${ROLE_NAME}" \
--architectures x86_64 \
--memory-size 256 \
--timeout 15 \
--region "$REGION"Aquí estamos indicando:
- El nombre de nuestra función.
- Que utilizaremos una imagen de contenedor.
- La ubicación de esa imagen en ECR.
- El rol de ejecución.
- La arquitectura del procesador.
- La memoria y el tiempo máximo de ejecución.
AWS comenzará a preparar nuestra función.
Esperamos hasta que esté activa:
1
2
3
aws lambda wait function-active-v2 \
--function-name "$PROJECT" \
--region "$REGION"Nota: la creación de roles IAM tiene propagación eventual. Si recibes un error indicando que Lambda no puede asumir el rol recién creado, comprueba la configuración del rol y vuelve a intentar la creación cuando el cambio se haya propagado.
¡Nuestra aplicación ya está desplegada en Lambda!
Pero necesitamos resolver el último problema.
¿Cómo recibirá solicitudes desde Internet?
Parte 6. Publicar nuestra API con API Gateway
26. ¿Para qué necesitamos API Gateway?
Nuestra función Lambda puede ejecutarse cuando recibe un evento.
Sin embargo, queremos ofrecer una API HTTP pública para que cualquier cliente autorizado pueda realizar solicitudes utilizando una URL.
API Gateway cumplirá esa función.
El recorrido será:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Cliente HTTP
|
v
API Gateway
|
v
AWS Lambda
|
v
Mangum
|
v
FastAPI
|
v
Respuesta HTTPAPI Gateway recibirá una solicitud HTTP y la enviará a nuestra función Lambda.
Mangum convertirá el evento en una solicitud compatible con FastAPI y convertirá la respuesta para que API Gateway pueda devolverla al cliente.
27. Crear una API HTTP
Utilizaremos la opción de creación rápida de API Gateway.
1
2
3
4
5
6
7
8
9
10
11
API_ID=$(
aws apigatewayv2 create-api \
--name "${PROJECT}-http" \
--protocol-type HTTP \
--target "$FUNCTION_ARN" \
--region "$REGION" \
--query ApiId \
--output text
)
echo "$API_ID"Este comando crea una API HTTP con integración Lambda, una ruta predeterminada
$default y un stage que publica automáticamente los cambios.La ruta
$default permitirá dirigir a Lambda las solicitudes de nuestros diferentes endpoints sin configurar cada ruta por separado.28. Autorizar a API Gateway para invocar Lambda
Aunque nuestra API ya existe, necesitamos otorgar permiso para que API Gateway invoque nuestra función.
Ejecutamos:
1
2
3
4
5
6
7
aws lambda add-permission \
--function-name "$PROJECT" \
--statement-id "allow-${PROJECT}-api-gateway" \
--action lambda:InvokeFunction \
--principal apigateway.amazonaws.com \
--source-arn "arn:aws:execute-api:${REGION}:${ACCOUNT_ID}:${API_ID}/*/*" \
--region "$REGION"Este permiso está limitado al identificador de nuestra API Gateway.
No estamos concediendo permisos indiscriminados a cualquier API Gateway.
29. Obtener nuestra URL pública
Consultamos la dirección de la API:
1
2
3
4
5
6
7
8
9
API_URL=$(
aws apigatewayv2 get-api \
--api-id "$API_ID" \
--region "$REGION" \
--query ApiEndpoint \
--output text
)
echo "$API_URL"Obtendremos una dirección similar a:
1
https://abc123xyz.execute-api.us-east-1.amazonaws.com¡Lo conseguimos!
Ya tenemos una URL pública.
30. Realizar nuestra primera solicitud desde Internet
Vamos a probar el endpoint de bienvenida.
1
curl "${API_URL}/"Resultado esperado:
1
2
3
{
"message": "Hello from FastAPI!"
}Ahora nuestro endpoint personalizado:
1
curl "${API_URL}/hello/Carlos"Resultado:
1
2
3
{
"message": "Hello, Carlos!"
}Probemos también nuestro POST:
1
2
3
4
curl -X POST \
"${API_URL}/greetings" \
-H "Content-Type: application/json" \
-d '{"name":"Ana"}'Resultado:
1
2
3
{
"message": "Hola, Ana!"
}Finalmente podemos abrir la documentación interactiva:
1
h t t p s ://abc123xyz.execute-api.us-east-1.amazonaws.com/docsSustituye la dirección de ejemplo por el valor real de
API_URL.¡Nuestra API ahora funciona desde Internet utilizando una arquitectura serverless!
Parte 7. Actualizar nuestra aplicación
31. ¿Qué ocurre cuando modificamos nuestro código?
Imaginemos que nuestro cliente nos pide realizar una modificación.
Actualizamos nuestra aplicación, pero ahora necesitamos desplegar la nueva versión.
Como estamos utilizando imágenes de contenedor, el procedimiento es sencillo.
Primero construimos una nueva imagen:
1
2
3
4
5
6
docker buildx build \
--platform linux/amd64 \
--provenance=false \
--load \
-f Dockerfile.lambda \
-t fastapi-workshop:lambda .La etiquetamos:
1
2
3
docker tag \
fastapi-workshop:lambda \
"$IMAGE_URI"La publicamos:
1
docker push "$IMAGE_URI"Y actualizamos Lambda:
1
2
3
4
aws lambda update-function-code \
--function-name "$PROJECT" \
--image-uri "$IMAGE_URI" \
--region "$REGION"Esperamos a que la actualización termine:
1
2
3
aws lambda wait function-updated-v2 \
--function-name "$PROJECT" \
--region "$REGION"Es importante comprender que AWS Lambda no actualiza automáticamente el código cuando subimos una imagen nueva con la misma etiqueta.
Debemos solicitar explícitamente la actualización de la función.
En proyectos reales también conviene utilizar etiquetas de versión únicas o identificadores de imagen inmutables para controlar los despliegues.
Parte 8. Monitoreo y solución de problemas
32. Consultar los registros de ejecución
En aplicaciones reales no siempre todo funciona a la primera.
Podemos encontrarnos con errores en nuestro código, dependencias incorrectas o problemas de permisos.
AWS Lambda integra su registro de ejecución con CloudWatch Logs.
Podemos revisar los registros desde la consola o utilizando AWS CLI:
1
2
3
4
5
aws logs tail \
"/aws/lambda/${PROJECT}" \
--since 10m \
--follow \
--region "$REGION"Algunos problemas comunes son:
| Error | Posible causa |
|---|---|
| 502 Bad Gateway | Error o respuesta inválida en la integración |
| 403 Forbidden | Permisos o autorización incorrectos |
| 404 Not Found | Ruta inexistente o mal configurada |
| Runtime.ImportModuleError | Problema de dependencias o importación |
| Task timed out | La función superó su tiempo máximo |
Los registros de Lambda y las métricas de API Gateway nos ayudarán a identificar la causa.
Parte 9. Presupuesto y buenas prácticas
33. ¿Cuánto cuesta ejecutar nuestra API?
Una de las ventajas de esta arquitectura es que no tenemos que contratar y mantener un servidor tradicional encendido permanentemente.
Sin embargo, serverless no significa que todo sea gratuito.
Los principales componentes que pueden generar costos son:
| Servicio | ¿Qué puede generar costos? |
|---|---|
| FastAPI | Framework gratuito |
| Docker Engine | Uso gratuito en entornos elegibles; otras herramientas o licencias pueden tener condiciones |
| Amazon ECR | Almacenamiento de imágenes y transferencia |
| AWS Lambda | Invocaciones y recursos consumidos |
| API Gateway | Solicitudes y transferencia |
| CloudWatch | Almacenamiento e ingesta de registros |
AWS ofrece cuotas gratuitas y, según la fecha de creación y el tipo de cuenta, posibles créditos promocionales.
Para realizar este workshop con pocas solicitudes, el costo puede ser muy bajo, pero no debemos asumir que será necesariamente cero.
Antes de comenzar, recomendamos crear un presupuesto con alertas mediante AWS Budgets.
34. Consideraciones de seguridad
Nuestra API fue publicada sin autenticación para facilitar el aprendizaje.
Esto es adecuado únicamente para una demostración controlada.
Para una aplicación de producción debemos considerar:
- Autenticación y autorización.
- Restricciones de acceso y límites de solicitudes.
- Principio de mínimo privilegio en IAM.
- Manejo seguro de secretos y variables de entorno.
- Validación de entradas.
- Actualización de dependencias e imágenes.
- Monitoreo, alertas y control de costos.
También debemos evaluar los tiempos de arranque de Lambda, especialmente cuando una función no ha sido utilizada recientemente.
Una arquitectura serverless es una herramienta poderosa, pero requiere tomar buenas decisiones de diseño.
Parte 10. Limpiar los recursos de AWS
35. Eliminar los recursos del workshop
Una buena práctica al terminar nuestros ejercicios es eliminar los recursos que ya no necesitamos.
Atención: los siguientes comandos eliminan recursos de AWS de manera permanente. Ejecútalos únicamente si estás seguro de que corresponden a tu workshop y no necesitas conservarlos.
Primero eliminamos API Gateway:
1
2
3
aws apigatewayv2 delete-api \
--api-id "$API_ID" \
--region "$REGION"Eliminamos nuestra función Lambda:
1
2
3
aws lambda delete-function \
--function-name "$PROJECT" \
--region "$REGION"Eliminamos el repositorio ECR y sus imágenes:
1
2
3
4
aws ecr delete-repository \
--repository-name "$PROJECT" \
--force \
--region "$REGION"Desvinculamos la política del rol:
1
2
3
aws iam detach-role-policy \
--role-name "$ROLE_NAME" \
--policy-arn arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRoleFinalmente eliminamos el rol:
1
2
aws iam delete-role \
--role-name "$ROLE_NAME"Los grupos de CloudWatch Logs pueden permanecer después de eliminar una función.
Si ya no necesitas los registros del ejercicio, también puedes eliminar el grupo correspondiente:
1
2
3
aws logs delete-log-group \
--log-group-name "/aws/lambda/${PROJECT}" \
--region "$REGION"Esto elimina permanentemente los registros de ese grupo.
Conclusión: de una idea a una API disponible en Internet
Regresemos al inicio de nuestra historia.
Teníamos una aplicación funcionando en nuestra computadora.
Nuestro cliente nos había preguntado:
“¿Cuál es la dirección para que nuestros usuarios puedan comenzar a utilizarla?”
En ese momento solamente teníamos:
localhost:8000Hoy, después de recorrer todo el proceso, tenemos una API desplegada utilizando servicios administrados de AWS.
Aprendimos a desarrollar endpoints con FastAPI, empaquetar nuestra aplicación con Docker, adaptarla utilizando Mangum, almacenar imágenes en Amazon ECR y ejecutar nuestra API en AWS Lambda.
Finalmente utilizamos API Gateway para hacerla accesible desde Internet.
Pero el verdadero aprendizaje fue comprender cómo se conecta todo el proceso de desarrollo y despliegue.
Ahora que conocemos este camino, podemos utilizarlo como base para proyectos más interesantes:
- Construir APIs para aplicaciones móviles.
- Crear microservicios empresariales.
- Desarrollar servicios para agentes de inteligencia artificial.
- Exponer modelos de machine learning.
- Integrar sistemas y automatizar procesos.
- Construir aplicaciones SaaS y arquitecturas basadas en eventos.
Porque desarrollar una aplicación no termina cuando conseguimos que funcione en nuestra computadora.
En muchos casos, ahí comienza el siguiente reto.
De localhost a la nube: un endpoint a la vez.
Enjoyed reading this content? Let the author know!
Your likes, comments, shares, and saves help creators reach more builders.
Loading recommendations
Loading article