Skip to main content
FastAPI es un framework web asíncrono para Python, y SQLAlchemy es el ORM de Python más utilizado. En esta guía, definirá un modelo tipado de SQLAlchemy, creará su tabla mediante una migración de Alembic y expondrá con FastAPI endpoints para crear, leer, actualizar y eliminar registros. La aplicación usa el controlador asyncpg, y todas las conexiones emplean TLS con verificación completa de certificados.

Requisitos previos

  • uv y Python 3.11 o posterior. Esta guía se ha probado con Python 3.13, FastAPI 0.142, SQLAlchemy 2.1, asyncpg 0.31, Alembic 1.20 y Uvicorn 0.54.
  • Una cuenta de ClickHouse Cloud
  • psql, para crear la base de datos. También puede ejecutar la sentencia CREATE DATABASE en la consola SQL.

Crear un servicio de ClickHouse Managed Postgres

En la consola de ClickHouse Cloud, haga clic en New service y seleccione Postgres. La instancia estará lista en unos minutos. Para ver el proceso paso a paso, consulte la guía de inicio rápido.

Obtenga los detalles de conexión

Haga clic en Conectar en la barra lateral izquierda de su servicio. El cuadro modal muestra su nombre de usuario, contraseña, servidor y puerto, e incluye un toggle Directly / via PgBouncer. Esta guía utiliza ambas conexiones:
  • PgBouncer (puerto 6432) para la aplicación. SQLAlchemy mantiene un grupo de conexiones en cada worker de Uvicorn, y el PgBouncer integrado permite que muchos workers y réplicas compartan un número reducido de conexiones de Postgres. Funciona en modo de agrupación por transacción (transaction pooling).
  • Directa (puerto 5432) para Alembic. Las migraciones ejecutan DDL, pueden prolongarse durante mucho tiempo y pueden depender del estado de la sesión, por lo que conviene que se comuniquen directamente con Postgres.
Active Use SSL y haga clic en Download CA certificate. También puede descargarlo desde Settings → CA Certificate. El certificado es exclusivo de su instancia, por lo que el controlador puede usarlo para verificar que se está comunicando con su servidor. Con Use SSL activado, la URL del cuadro modal termina en sslmode=verify-full&sslrootcert=.... asyncpg no lee estos parámetros, así que copie únicamente la contraseña y el host del cuadro modal. En su lugar, la aplicación pasa el certificado de la CA desde el código (consulte Configurar la conexión).

Configurar el proyecto

Cree un proyecto e instale FastAPI, Uvicorn, SQLAlchemy con el extra asyncio, asyncpg y Alembic:
¿Va a añadirlo a una aplicación existente?Omita los comandos uv init y mkdir. En su proyecto, ejecute uv add "sqlalchemy[asyncio]" asyncpg alembic y continúe con Configurar la conexión. Si su aplicación usa SQLite mediante SQLAlchemy asíncrono:
  • Modifique su llamada a create_async_engine para que coincida con la de app/db.py. En migrations/env.py, importe Base y sus modelos desde sus propios módulos.
  • Elimine Base.metadata.create_all del código de inicio. Alembic crea sus tablas existentes en Postgres, pero no copia los datos de SQLite.

Configurar la conexión

Mueva el certificado de CA que descargó al directorio del proyecto y cámbiele el nombre a ca-certificate.pem. Cree una base de datos para la aplicación. Sustituya <PASSWORD> y el host por los valores que aparecen en el cuadro de diálogo Conectar:
Cree un archivo .env en la raíz del proyecto o agregue estas variables al que ya tenga. Las dos URL solo se diferencian en el puerto, utilizan el esquema postgresql+asyncpg y no incluyen parámetros de SSL:
.env
La ruta de DATABASE_CA_CERT se resuelve en relación con el directorio desde el que ejecuta los comandos. Si su contraseña contiene caracteres especiales como @, / o #, aplíqueles codificación URL. No incluya .env en el control de versiones. Cree app/db.py. Este archivo define la configuración de TLS, el motor y una fábrica de sesiones que comparte el resto de la aplicación:
app/db.py
Un ssl.SSLContext creado con PROTOCOL_TLS_CLIENT exige un certificado válido y comprueba el nombre del host, lo que equivale a sslmode=verify-full. SQLAlchemy lo pasa a asyncpg a través de connect_args.
Pase el certificado de la CA en connect_args
  • Sin un argumento ssl, asyncpg usa TLS si el servidor lo ofrece, pero no verifica el certificado.
  • No copie sslmode ni sslrootcert del modal Conectar a la URL. SQLAlchemy pasa los parámetros de la URL a asyncpg como argumentos de palabra clave, y la conexión falla con TypeError: connect() got an unexpected keyword argument 'sslmode'.
  • En Python 3.13 y versiones posteriores, un contexto creado con ssl.create_default_context falla con certificate verify failed: Missing Authority Key Identifier, ya que esa función habilita comprobaciones X.509 estrictas. Cree el contexto con ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT), tal como se muestra.
  • Si el certificado de la CA es incorrecto, la conexión falla con certificate verify failed: unable to get local issuer certificate. Si el nombre del host no coincide (por ejemplo, al conectarse mediante una dirección IP), falla con certificate verify failed: IP address mismatch.
Si prefiere mantener la configuración de TLS en la URL, o si su aplicación usa sesiones síncronas de SQLAlchemy, utilice el controlador psycopg en lugar de asyncpg. Está basado en libpq y acepta los parámetros del modal Conectar, por ejemplo postgresql+psycopg://...:5432/guide_fastapi?sslmode=verify-full&sslrootcert=ca-certificate.pem, tanto con create_engine como con create_async_engine.

Definir el modelo

Cree app/models.py con un modelo tipado de SQLAlchemy:
app/models.py

Ejecutar migraciones

Cree un entorno de Alembic a partir de la plantilla async:
Reemplace el contenido del archivo migrations/env.py generado. La nueva versión carga sus modelos para que autogenerate pueda usarlos y se conecta directamente a Postgres con la misma configuración de TLS que la aplicación:
migrations/env.py
Alembic ignora el valor de marcador de posición sqlalchemy.url de alembic.ini, ya que env.py crea el motor por su cuenta. Genere una migración a partir del modelo y, a continuación, aplíquela:
Revise el archivo generado en migrations/versions y haga commit de este junto con su código. Alembic registra la revisión aplicada en la tabla alembic_version. Cada vez que modifique un modelo, vuelva a ejecutar revision --autogenerate y upgrade head.

Añadir los endpoints de la API

Cree app/main.py. Cada solicitud obtiene su propia AsyncSession a través de una dependencia de FastAPI, y el grupo de conexiones del motor se cierra al apagarse la aplicación:
app/main.py
SQLAlchemy ejecuta las sentencias de cada sesión en una única transacción, que finaliza con session.commit. Cuando se cierra la sesión, se revierte cualquier trabajo sin commit. Al hacer un INSERT, SQLAlchemy recupera los valores generados de id y created_at mediante RETURNING, por lo que create_todo no necesita una consulta adicional.

Ejecutar y verificar

Inicie la aplicación:
En una segunda terminal, crea dos tareas, marca la primera como completada y elimina la segunda:
La solicitud DELETE devuelve 204 No Content. Si se solicita una tarea que no existe, se devuelve 404:
Abra http://localhost:3112/todos en su navegador para ver la lista de tareas pendientes, o http://localhost:3112/docs para probar los endpoints en la documentación interactiva de la API de FastAPI. Para confirmar que la conexión directa utiliza TLS verificado, consulte pg_stat_ssl con la misma configuración de TLS que usa la aplicación:
Para ver los datos en la consola, abra consola SQL en la barra lateral izquierda de su servicio, expanda guide_fastapi y después public, y haga clic en la tabla todos. La tabla alembic_version, que aparece junto a ella, almacena el historial de migraciones. La tabla todos contiene la fila restante: Detenga la aplicación con Ctrl+C.

Uso de PgBouncer

La aplicación funciona a través de PgBouncer con la configuración predeterminada de asyncpg y SQLAlchemy:
  • asyncpg ejecuta cada consulta como una sentencia preparada con nombre a nivel de protocolo, y SQLAlchemy almacena en caché hasta 100 de ellas por conexión. El PgBouncer incluido admite estas sentencias en el modo de agrupación por transacciones, y asyncpg las libera con un mensaje Close a nivel de protocolo en lugar de con la sentencia SQL DEALLOCATE. No necesita statement_cache_size=0 ni una prepared_statement_name_func personalizada. Si aun así desactiva la caché de sentencias de asyncpg, añada también prepared_statement_cache_size=0 a la URL para desactivar la caché de SQLAlchemy.
  • La configuración de sesión establecida con SET, como search_path, no se conserva en la siguiente transacción y puede filtrarse a otros clientes. En su lugar, utilice SET LOCAL dentro de una transacción.
  • pg_stat_ssl informa sobre la conexión entre PgBouncer y Postgres, por lo que la comprobación de TLS anterior devuelve (False, None) a través del puerto 6432. Aun así, la conexión de la aplicación con PgBouncer se sigue verificando: un certificado de CA incorrecto falla de la misma manera.
  • Alembic no adquiere bloqueos consultivos y ejecuta cada upgrade en una sola transacción, por lo que también funciona a través de PgBouncer. Aun así, para migraciones largas es preferible usar la conexión directa.
Cambio del tipo de una columnaPgBouncer conserva las sentencias preparadas en sus conexiones con Postgres. Si una migración cambia el tipo de una columna que devuelve una consulta, por ejemplo de integer a bigint, esa consulta falla a través de PgBouncer con InvalidCachedStatementError: cached statement plan is invalid due to a database schema or configuration change, incluso desde conexiones nuevas, hasta que PgBouncer reemplace sus conexiones con Postgres. Para solucionarlo, cierre las conexiones de PgBouncer con Postgres para esa base de datos a través de la conexión directa, con el mismo usuario que utiliza la aplicación para conectarse. PgBouncer se conecta a Postgres localmente, por lo que sus conexiones no tienen client_addr; la consulta no afecta a las conexiones directas. PgBouncer abre conexiones nuevas, y cada conexión agrupada de la aplicación puede fallar una vez más antes de que SQLAlchemy actualice su caché:
Añadir una columna no provoca este problema, porque SQLAlchemy enumera explícitamente las columnas que selecciona.

Próximos pasos

  • Conexión: cadenas de conexión, PgBouncer y TLS
  • Configuración: parámetros de Postgres y PgBouncer, como max_connections
  • Réplicas de lectura: envíe consultas de solo lectura a una réplica con un segundo motor
  • Seguridad: listas de acceso por IP y redes privadas