Skip to main content
FastAPI é um framework web assíncrono para Python, e SQLAlchemy é o ORM mais usado em Python. Neste guia, você define um model SQLAlchemy tipado, cria a tabela correspondente com uma migração do Alembic e expõe endpoints de criação, leitura, atualização e exclusão com o FastAPI. A aplicação usa o driver asyncpg, e todas as conexões usam TLS com verificação de certificados completa.

Pré-requisitos

  • uv e Python 3.11 ou superior. Este guia foi testado com Python 3.13, FastAPI 0.142, SQLAlchemy 2.1, asyncpg 0.31, Alembic 1.20 e Uvicorn 0.54.
  • Uma conta no ClickHouse Cloud
  • psql, para criar o banco de dados. Você também pode executar a instrução CREATE DATABASE no SQL Console.

Crie um serviço ClickHouse Managed Postgres

No console do ClickHouse Cloud, clique em New service e selecione Postgres. A instância fica pronta em poucos minutos. Para um passo a passo, consulte o guia de início rápido.

Obtenha os detalhes de conexão

Clique em Connect na barra lateral esquerda do seu serviço. O modal exibe seu nome de usuário, senha, servidor e porta, além de um seletor Directly / via PgBouncer. Este guia usa as duas conexões:
  • PgBouncer (porta 6432) para a aplicação. O SQLAlchemy mantém um pool de conexões em cada worker do Uvicorn, e o PgBouncer incluído permite que vários workers e réplicas compartilhem um número reduzido de conexões com o Postgres. Ele opera no modo de pooling de transações.
  • Direta (porta 5432) para o Alembic. As migrations executam DDL, podem demorar bastante e podem depender do estado da sessão, por isso devem se comunicar diretamente com o Postgres.
Ative Use SSL e clique em Download CA certificate. Você também pode baixá-lo em Settings → CA Certificate. O certificado é exclusivo da sua instância, o que permite ao driver usá-lo para verificar se está se comunicando com o seu servidor. Com Use SSL ativado, a URL exibida no modal termina em sslmode=verify-full&sslrootcert=.... O asyncpg não lê esses parâmetros, então copie do modal apenas a senha e o host. Em vez disso, a aplicação passa o certificado da CA no próprio código (consulte Configure a conexão).

Configure o projeto

Crie um projeto e instale FastAPI, Uvicorn, SQLAlchemy com o extra asyncio, asyncpg e Alembic:
Vai adicionar a um app existente?Pule os comandos uv init e mkdir. No seu projeto, execute uv add "sqlalchemy[asyncio]" asyncpg alembic e, em seguida, siga para Configurar a conexão. Se o seu app usa SQLite com SQLAlchemy assíncrono:
  • Altere sua chamada a create_async_engine para que fique igual à de app/db.py. Em migrations/env.py, importe Base e seus models a partir dos seus próprios módulos.
  • Remova Base.metadata.create_all da inicialização. O Alembic cria suas tabelas existentes no Postgres, mas não copia os dados do SQLite.

Configure a conexão

Mova o certificado da CA que você baixou para o diretório do projeto e renomeie-o para ca-certificate.pem. Crie um banco de dados para a aplicação. Substitua <PASSWORD> e o host pelos valores exibidos no modal Connect:
Crie um arquivo .env na raiz do projeto ou adicione estas variáveis ao arquivo que você já tem. As duas URLs diferem apenas na porta, usam o esquema postgresql+asyncpg e não incluem parâmetros de SSL:
.env
O caminho em DATABASE_CA_CERT é resolvido em relação ao diretório a partir do qual você executa os comandos. Se sua senha contiver caracteres especiais como @, / ou #, aplique a codificação de URL a eles. Mantenha o .env fora do controle de versão. Crie app/db.py. Esse arquivo monta as configurações de TLS, o motor e uma fábrica de sessões compartilhada pelo restante da aplicação:
app/db.py
Um ssl.SSLContext criado com PROTOCOL_TLS_CLIENT exige um certificado válido e verifica o hostname, o que equivale a sslmode=verify-full. O SQLAlchemy o repassa ao asyncpg por meio de connect_args.
Passe o certificado da CA em connect_args
  • Sem um argumento ssl, o asyncpg usa TLS se o servidor oferecer suporte, mas não verifica o certificado.
  • Não copie sslmode nem sslrootcert do modal Connect para a URL. O SQLAlchemy repassa os parâmetros de URL ao asyncpg como argumentos nomeados, e a conexão falha com TypeError: connect() got an unexpected keyword argument 'sslmode'.
  • No Python 3.13 e versões posteriores, um contexto criado com ssl.create_default_context falha com certificate verify failed: Missing Authority Key Identifier, porque essa função habilita verificações X.509 estritas. Crie o contexto com ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT), como mostrado.
  • Se o certificado da CA estiver incorreto, a conexão falha com certificate verify failed: unable to get local issuer certificate. Se o hostname não corresponder (por exemplo, ao se conectar por endereço IP), a conexão falha com certificate verify failed: IP address mismatch.
Se preferir manter as configurações de TLS na URL, ou se sua aplicação usa sessões síncronas do SQLAlchemy, use o driver psycopg em vez do asyncpg. Ele é baseado na libpq e aceita os parâmetros do modal Connect, por exemplo, postgresql+psycopg://...:5432/guide_fastapi?sslmode=verify-full&sslrootcert=ca-certificate.pem, tanto com create_engine quanto com create_async_engine.

Defina o model

Crie o arquivo app/models.py com um model SQLAlchemy tipado:
app/models.py

Executar as migrations

Crie um ambiente do Alembic a partir do template async:
Substitua o conteúdo do arquivo migrations/env.py gerado. A nova versão carrega seus models para o autogenerate e se conecta diretamente ao Postgres usando as mesmas configurações de TLS da aplicação:
migrations/env.py
O Alembic ignora o valor de exemplo sqlalchemy.url em alembic.ini, pois o env.py cria o motor por conta própria. Gere uma migração a partir do model e, em seguida, aplique-a:
Revise o arquivo gerado em migrations/versions e faça o commit dele junto com o seu código. O Alembic registra a revisão aplicada na tabela alembic_version. Sempre que alterar um model, execute novamente revision --autogenerate e upgrade head.

Adicione os endpoints da API

Crie app/main.py. Cada requisição recebe sua própria AsyncSession por meio de uma dependência do FastAPI, e o pool do motor é fechado quando a aplicação é encerrada:
app/main.py
O SQLAlchemy executa as instruções de cada sessão em uma única transação, que termina em session.commit. Quando a sessão é fechada, todo trabalho não confirmado é revertido. Em um INSERT, o SQLAlchemy recupera o id e o created_at gerados por meio de RETURNING, de modo que create_todo não precisa de uma consulta adicional.

Executar e verificar

Inicie o aplicativo:
Em um segundo terminal, crie duas tarefas, marque a primeira como concluída e exclua a segunda:
A requisição DELETE retorna 204 No Content. Requisitar um todo que não existe retorna 404:
Abra http://localhost:3112/todos no navegador para listar as tarefas pendentes, ou http://localhost:3112/docs para testar os endpoints na documentação interativa da API do FastAPI. Para confirmar que a conexão direta usa TLS verificado, consulte pg_stat_ssl com as mesmas configurações de TLS usadas pelo aplicativo:
Para ver os dados no console, abra o SQL Console na barra lateral esquerda do seu serviço, expanda guide_fastapi e depois public e clique na tabela todos. A tabela alembic_version, logo ao lado, armazena o histórico de migrations. A tabela todos contém a linha restante: Encerre a aplicação com Ctrl+C.

Usando o PgBouncer

A aplicação funciona por meio do PgBouncer com as configurações padrão do asyncpg e do SQLAlchemy:
  • O asyncpg executa cada consulta como um prepared statement nomeado, no nível do protocolo, e o SQLAlchemy armazena em cache até 100 deles por conexão. O PgBouncer incluído oferece suporte a essas instruções no modo de pooling de transações, e o asyncpg as libera com uma mensagem Close no nível do protocolo, em vez de usar o DEALLOCATE do SQL. Você não precisa de statement_cache_size=0 nem de uma prepared_statement_name_func personalizada. Se, ainda assim, você desativar o cache de instruções do asyncpg, adicione também prepared_statement_cache_size=0 à URL para desativar o cache do SQLAlchemy.
  • As configurações de sessão definidas com SET, como search_path, não são mantidas na transação seguinte e podem vazar para outros clientes. Em vez disso, use SET LOCAL dentro de uma transação.
  • pg_stat_ssl reporta a conexão entre o PgBouncer e o Postgres, portanto a verificação de TLS acima retorna (False, None) pela porta 6432. A conexão da aplicação com o PgBouncer continua sendo verificada: um certificado de CA incorreto falha da mesma forma.
  • O Alembic não adquire advisory locks e executa cada upgrade em uma única transação, por isso também funciona por meio do PgBouncer. Ainda assim, a conexão direta é a melhor opção para migrations longas.
Alterando o tipo de uma colunaO PgBouncer mantém os prepared statements em suas conexões com o Postgres. Depois que uma migração altera o tipo de uma coluna retornada por uma consulta, como de integer para bigint, essa consulta passa a falhar via PgBouncer com InvalidCachedStatementError: cached statement plan is invalid due to a database schema or configuration change, mesmo em novas conexões, até que o PgBouncer substitua suas conexões com o Postgres. Para resolver, encerre as conexões do PgBouncer com o Postgres para o banco de dados usando a conexão direta, com o mesmo usuário que a aplicação utiliza para se conectar. O PgBouncer se conecta ao Postgres localmente, portanto suas conexões não têm client_addr; a consulta não afeta as conexões diretas. O PgBouncer então abre novas conexões, e cada conexão da aplicação no pool pode falhar mais uma vez antes que o SQLAlchemy atualize seu cache:
Adicionar uma coluna não causa esse problema, porque o SQLAlchemy lista explicitamente as colunas que seleciona.

Próximos passos