---
title: "Alembic com SQLAlchemy e FastAPI: migrações sem susto"
url: "https://python.dev.br/guias/alembic-migrations-sqlalchemy-fastapi/"
markdown_url: "https://python.dev.br/guias/alembic-migrations-sqlalchemy-fastapi.MD"
description: "Aprenda a configurar Alembic em projetos Python com SQLAlchemy e FastAPI para criar, revisar e aplicar migrações de banco com segurança."
date: "2026-06-04"
author: "Equipe Python Brasil"
---

# Alembic com SQLAlchemy e FastAPI: migrações sem susto

Aprenda a configurar Alembic em projetos Python com SQLAlchemy e FastAPI para criar, revisar e aplicar migrações de banco com segurança.


Mudar tabela no banco de dados é uma das tarefas que mais separam um projeto de estudo de uma aplicação profissional. Enquanto o sistema está pequeno, parece aceitável apagar o arquivo SQLite, recriar a tabela ou rodar um `CREATE TABLE` manual. Em uma API usada por clientes, por um time interno ou por um teste técnico mais sério, esse improviso vira risco: dados somem, ambientes ficam diferentes e ninguém sabe exatamente qual alteração foi aplicada.

O Alembic é a ferramenta de migrações mais comum para projetos Python que usam [SQLAlchemy](/glossario/sqlalchemy/). Ele registra mudanças no schema do banco em arquivos versionados, permite aplicar e desfazer alterações e ajuda a manter desenvolvimento, homologação e produção seguindo a mesma sequência. Para quem trabalha com [FastAPI](/glossario/fastapi/), Flask ou serviços backend em Python, dominar esse fluxo aumenta muito a confiança na hora de evoluir tabelas, índices e relacionamentos.

Neste guia, vamos configurar Alembic em uma API simples com SQLAlchemy 2.0, criar a primeira migração, revisar o arquivo gerado, aplicar no banco e organizar cuidados para produção. Se você ainda está montando a base da API, leia também [criando API com FastAPI](/guias/criando-api-fastapi/), [SQLAlchemy 2.0 moderno](/blog/sqlalchemy-2-orm-moderno-python/) e [PostgreSQL com Python](/blog/python-e-postgresql/). Para transformar isso em portfólio, combine com [testes com pytest](/guias/testes-com-pytest/) e [projetos de portfólio Python](/carreira/projetos-portfolio-python/). Em times que também usam Go, vale comparar o mesmo problema com stacks de backend em <a href="https://golang.com.br/" target="_blank" rel="noopener" onclick="umami.track('portfolio-site-click', { destination: 'golang.com.br' })">Golang Brasil</a>.

## Quando usar Alembic

Use Alembic quando seu projeto tem banco relacional e o schema precisa evoluir sem perder dados. Alguns exemplos comuns:

- adicionar uma coluna `status` em pedidos existentes;
- criar índice para melhorar uma busca frequente;
- renomear uma tabela sem quebrar a API;
- criar relacionamento entre usuário e organização;
- aplicar a mesma mudança em desenvolvimento, staging e produção;
- deixar claro no Git quando e por que o banco mudou.

Se o projeto é um script descartável, uma prova rápida ou um notebook temporário, talvez Alembic seja excesso. Mas se existe API, autenticação, dados de usuários, deploy contínuo ou mais de uma pessoa trabalhando, migrações deixam de ser luxo e viram parte básica da engenharia.

## Estrutura do projeto

Comece com uma estrutura simples:

```text
api-tarefas/
  alembic.ini
  pyproject.toml
  app/
    __init__.py
    database.py
    models.py
    main.py
  migrations/
    env.py
    script.py.mako
    versions/
```

Instale as dependências:

```bash
python -m venv .venv
source .venv/bin/activate
pip install fastapi uvicorn sqlalchemy alembic psycopg[binary]
```

Para desenvolvimento local, você pode começar com SQLite. Para simular um cenário mais próximo do mercado brasileiro de backend, PostgreSQL é melhor porque aparece com frequência em vagas, testes técnicos e sistemas internos.

## Modelo SQLAlchemy

Um modelo mínimo de tarefa pode ficar em `app/models.py`:

```python
from datetime import datetime

from sqlalchemy import Boolean, DateTime, String
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column


class Base(DeclarativeBase):
    pass


class Tarefa(Base):
    __tablename__ = "tarefas"

    id: Mapped[int] = mapped_column(primary_key=True)
    titulo: Mapped[str] = mapped_column(String(160), nullable=False)
    concluida: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
    criada_em: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow, nullable=False)
```

O ponto mais importante para o Alembic é ter uma `Base` declarativa que concentra o metadata dos modelos. É esse metadata que a geração automática compara com o banco para descobrir tabelas, colunas e índices.

Em `app/database.py`, deixe a URL do banco centralizada:

```python
import os

from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker


DATABASE_URL = os.getenv("DATABASE_URL", "postgresql+psycopg://postgres:postgres@localhost:5432/app")

engine = create_engine(DATABASE_URL, pool_pre_ping=True)
SessionLocal = sessionmaker(bind=engine, autoflush=False, autocommit=False)
```

Em produção, não coloque senha real no código. Use variável de ambiente, secret manager ou configuração segura do provedor. O arquivo `.env.example` pode mostrar o formato sem expor credenciais.

## Inicializando o Alembic

Rode o comando na raiz do projeto:

```bash
alembic init migrations
```

Isso cria `alembic.ini` e a pasta `migrations/`. Em seguida, ajuste `migrations/env.py` para importar os modelos e usar a URL do projeto:

```python
from alembic import context
from sqlalchemy import engine_from_config, pool

from app.database import DATABASE_URL
from app.models import Base

config = context.config
config.set_main_option("sqlalchemy.url", DATABASE_URL)
target_metadata = Base.metadata
```

O arquivo completo gerado pelo Alembic tem funções para modo offline e online. Você não precisa reescrever tudo: normalmente basta importar `Base`, definir `target_metadata` e garantir que a connection string venha da mesma fonte usada pela aplicação.

## Primeira migração

Com o modelo criado e o `env.py` configurado, gere a migração inicial:

```bash
alembic revision --autogenerate -m "cria tabela de tarefas"
```

O Alembic criará um arquivo em `migrations/versions/`, com nome parecido com:

```text
20260604_1015_cria_tabela_de_tarefas.py
```

Abra o arquivo antes de aplicar. Uma migração inicial típica terá algo assim:

```python
def upgrade() -> None:
    op.create_table(
        "tarefas",
        sa.Column("id", sa.Integer(), nullable=False),
        sa.Column("titulo", sa.String(length=160), nullable=False),
        sa.Column("concluida", sa.Boolean(), nullable=False),
        sa.Column("criada_em", sa.DateTime(), nullable=False),
        sa.PrimaryKeyConstraint("id"),
    )


def downgrade() -> None:
    op.drop_table("tarefas")
```

Nunca trate `--autogenerate` como piloto automático. Ele ajuda muito, mas não entende intenção de negócio. Se você renomeia uma coluna, por exemplo, ele pode interpretar como “remove coluna antiga e cria coluna nova”, o que apagaria dados. Nesses casos, edite a migração manualmente com `op.rename_column` ou comandos equivalentes.

## Aplicando no banco

Para aplicar todas as migrações pendentes:

```bash
alembic upgrade head
```

Para ver o histórico:

```bash
alembic history
```

Para ver a versão atual do banco:

```bash
alembic current
```

O Alembic cria uma tabela chamada `alembic_version`. Ela guarda qual revisão está aplicada naquele banco. Não apague essa tabela manualmente, porque ela é o controle de versão do schema.

## Alterando o schema com segurança

Imagine que agora a tarefa precisa de prioridade. Você altera o modelo:

```python
prioridade: Mapped[str] = mapped_column(String(20), default="media", nullable=False)
```

Depois gera nova migração:

```bash
alembic revision --autogenerate -m "adiciona prioridade em tarefas"
```

O arquivo gerado deve incluir algo como:

```python
def upgrade() -> None:
    op.add_column("tarefas", sa.Column("prioridade", sa.String(length=20), nullable=False))


def downgrade() -> None:
    op.drop_column("tarefas", "prioridade")
```

Aqui existe uma armadilha: se a tabela já tem linhas em produção, adicionar uma coluna `nullable=False` sem valor padrão pode falhar. Uma alternativa segura é aplicar em etapas:

```python
def upgrade() -> None:
    op.add_column("tarefas", sa.Column("prioridade", sa.String(length=20), nullable=True))
    op.execute("UPDATE tarefas SET prioridade = 'media' WHERE prioridade IS NULL")
    op.alter_column("tarefas", "prioridade", nullable=False)
```

Esse cuidado mostra maturidade em teste técnico e no trabalho real. Você não está apenas “fazendo rodar”; está protegendo dados existentes.

## Integração com FastAPI

O FastAPI não precisa criar tabelas automaticamente se você usa Alembic. Evite colocar `Base.metadata.create_all(engine)` no startup da aplicação em projetos versionados. Esse comando é útil em exemplos pequenos, mas em produção ele mistura responsabilidades e pula revisão de migração.

Um fluxo mais profissional é:

- modelos SQLAlchemy descrevem o schema desejado;
- Alembic registra mudanças versionadas;
- CI ou deploy roda `alembic upgrade head` antes de subir a nova versão;
- a aplicação assume que o banco já está na versão correta.

Se você usa Docker, pode ter um comando separado para migração:

```bash
docker compose run --rm api alembic upgrade head
docker compose up -d api
```

Em ambientes maiores, a migração pode ser um job de deploy. O importante é evitar que várias instâncias da API tentem migrar o banco ao mesmo tempo sem coordenação.

## Boas práticas para produção

Antes de aplicar migrações em produção, revise alguns pontos:

- a migração foi lida manualmente por alguém;
- existe backup ou snapshot recente;
- mudanças destrutivas foram evitadas ou planejadas;
- índices grandes foram avaliados para não travar o banco em horário crítico;
- o deploy da aplicação é compatível com o schema antigo e novo, quando necessário;
- o downgrade existe, mesmo que o plano real seja corrigir para frente.

Nem todo rollback de banco é simples. Apagar uma coluna e depois tentar desfazer não recupera os dados perdidos. Por isso, em sistemas importantes, prefira migrações reversíveis, deploys em duas etapas e alterações compatíveis com a versão anterior da aplicação.

## Erros comuns

Um erro frequente é esquecer de importar todos os modelos no `env.py`. Se o Alembic não conhece uma classe, ele não gera tabela para ela. Em projetos maiores, crie um módulo que importa todos os modelos ou garanta que o pacote de modelos seja carregado antes de definir `target_metadata`.

Outro erro é editar o banco manualmente e depois tentar “sincronizar” o Alembic. Isso cria divergência entre Git, ambientes e produção. Se uma correção manual foi inevitável, registre uma migração equivalente ou use comandos como `alembic stamp` apenas com entendimento claro do histórico.

Também evite nomes genéricos como `fix`, `update` ou `migration`. Uma mensagem como `adiciona_indice_em_tarefas_status` ajuda revisores, colegas e você mesmo daqui a três meses.

## Checklist de portfólio

Para mostrar Alembic em um projeto público, inclua no README:

- comando para subir PostgreSQL local;
- exemplo de `DATABASE_URL` sem senha real;
- comando `alembic upgrade head`;
- explicação de onde ficam os modelos;
- pelo menos duas migrações no histórico;
- teste ou script que confirma que a API roda depois da migração.

Esse tipo de detalhe conversa diretamente com vagas backend, engenharia de dados leve, RevOps técnico e automação corporativa. Muitas pessoas sabem criar endpoint; menos pessoas demonstram cuidado com schema, dados existentes e deploy.

## Conclusão

Alembic não é apenas mais uma dependência. Ele é o contrato entre o código Python e o banco relacional. Quando você versiona migrações, revisa arquivos gerados e aplica mudanças em uma ordem previsível, o projeto deixa de depender de memória, comandos manuais e sorte.

Para começar bem, configure `target_metadata`, gere a migração inicial, leia o arquivo antes de aplicar e trate mudanças em produção com cuidado. Depois, evolua para CI, Docker, testes e deploys em etapas. Esse fluxo torna sua API FastAPI mais confiável e também deixa seu portfólio mais próximo do que equipes profissionais esperam de uma pessoa desenvolvedora Python.
