Encurtador de URLs minimalista construído com FastAPI, PostgreSQL, Docker e deploy automático no Render.
- Visão Geral
- Funcionalidades
- Stack
- Arquitetura
- Estrutura do Projeto
- Como Rodar Localmente
- Endpoints da API
- Testes
- CI/CD
- Deploy no Render
- Variáveis de Ambiente
URL.SHORT é um serviço de encurtamento de URLs com interface visual minimalista. Você cola uma URL longa, recebe um código curto de 6 caracteres, e consegue acompanhar quantas vezes o link foi acessado.
Demo: url-shortener.onrender.com
- Encurtamento de URLs — gera códigos curtos de 6 caracteres alfanuméricos
- Redirecionamento — redireciona para a URL original com status 302
- Estatísticas — rastreia e exibe o número de acessos por link
- Idempotência — a mesma URL sempre retorna o mesmo código curto
- Interface visual — frontend minimalista servido pelo próprio FastAPI
- Validação — rejeita URLs inválidas com mensagens de erro claras
- Histórico local — links encurtados salvos no localStorage do browser
| Camada | Tecnologia |
|---|---|
| API | FastAPI 0.111 |
| Banco de dados | PostgreSQL 16 + SQLAlchemy 2.0 |
| Validação | Pydantic v2 |
| Servidor | Uvicorn |
| Containerização | Docker + Docker Compose |
| Testes | pytest + SQLite in-memory |
| CI/CD | GitHub Actions |
| Deploy | Render |
| Frontend | HTML + CSS + JS puro |
Usuário
│
├── POST /shorten ──────► FastAPI ──► PostgreSQL
│ │
├── GET /{code} ◄────────────────── (busca URL)
│ │
│ └──► 302 Redirect ──► URL original
│
└── GET /stats/{code} ──► { access_count, created_at, ... }
Fluxo de desenvolvimento:
git push ──► GitHub Actions (testes + build) ──► Render (deploy automático)
url-shortener/
├── .github/
│ └── workflows/
│ └── ci.yml # pipeline CI/CD
├── app/
│ ├── __init__.py
│ ├── main.py # entrypoint FastAPI + rotas
│ ├── models.py # modelos SQLAlchemy (URL, Access)
│ ├── schemas.py # schemas Pydantic
│ ├── crud.py # operações no banco
│ ├── database.py # conexão e sessão
│ └── config.py # variáveis de ambiente
├── frontend/
│ └── index.html # interface visual
├── tests/
│ ├── __init__.py
│ ├── conftest.py # fixtures pytest
│ └── test_main.py # 8 testes de integração
├── .env.example # template de variáveis
├── .gitignore
├── conftest.py # raiz do projeto para pytest
├── docker-compose.yml # app + banco local
├── Dockerfile
├── pytest.ini
├── render.yaml # configuração do Render
└── requirements.txt
- Docker Desktop instalado e rodando
git clone https://github.com/SEU_USUARIO/url-shortener.git
cd url-shortenercp .env.example .envO .env.example já vem com os valores corretos para desenvolvimento local — não precisa alterar nada.
docker compose up --buildIsso vai subir dois containers:
- db — PostgreSQL 16
- app — FastAPI com hot reload
| Serviço | URL |
|---|---|
| Interface visual | http://localhost:8000 |
| Documentação API (Swagger) | http://localhost:8000/docs |
| Health check | http://localhost:8000/health |
Para rodar em background:
docker compose up --build -d
docker compose down # para pararEncurta uma URL.
Request:
{
"original_url": "https://www.exemplo.com/url-muito-longa"
}Response 201:
{
"short_code": "aB3xKz",
"short_url": "https://url-shortener.onrender.com/aB3xKz",
"original_url": "https://www.exemplo.com/url-muito-longa",
"created_at": "2024-01-15T10:30:00Z"
}Redireciona para a URL original.
Response 302: redireciona para a URL original e registra o acesso.
Response 404:
{ "detail": "URL não encontrada" }Retorna estatísticas de acesso.
Response 200:
{
"short_code": "aB3xKz",
"original_url": "https://www.exemplo.com/url-muito-longa",
"access_count": 42,
"created_at": "2024-01-15T10:30:00Z",
"is_active": true
}Health check para monitoramento.
Response 200:
{ "status": "ok" }Os testes rodam com SQLite em memória — sem precisar de Docker ou PostgreSQL.
# ative o venv
python -m venv .venv
.venv\Scripts\activate # Windows
source .venv/bin/activate # Linux/Mac
# instale as dependências
pip install -r requirements.txt
# rode os testes
pytest -vCobertura dos testes:
| Teste | O que valida |
|---|---|
test_health_check |
endpoint de health |
test_shorten_url |
encurtamento básico |
test_shorten_same_url_twice |
idempotência |
test_shorten_invalid_url |
validação de URL inválida |
test_redirect |
redirecionamento 302 |
test_redirect_not_found |
código inexistente retorna 404 |
test_stats |
contagem de acessos |
test_stats_not_found |
stats de código inexistente |
O pipeline roda automaticamente em todo push para main ou develop, e em pull requests.
push/PR
│
├── job: test
│ ├── checkout
│ ├── setup Python 3.11
│ ├── pip install (com cache)
│ └── pytest -v
│
└── job: build (só roda se test passar)
├── checkout
└── docker build
Configuração: .github/workflows/ci.yml
Regras de branch:
main— protegida, requer PR + CI verde para mergedevelop— branch de integraçãofeature/*— branches de trabalho
O deploy é automático a cada push na branch main.
- Crie um banco PostgreSQL no Render (free tier)
- Crie um Web Service conectado a este repositório
- Runtime: Docker
- Branch:
main - Start Command:
uvicorn app.main:app --host 0.0.0.0 --port 10000
- Configure as variáveis de ambiente (ver seção abaixo)
O projeto inclui render.yaml na raiz para configuração declarativa da infraestrutura.
Copie .env.example para .env e preencha:
# URL de conexão com o PostgreSQL
# Local (Docker): postgresql://postgres:postgres@db:5432/urlshortener
# Produção: fornecida pelo Render
DATABASE_URL=postgresql://postgres:postgres@db:5432/urlshortener
# Chave secreta da aplicação
# Gere com: python -c "import secrets; print(secrets.token_hex(32))"
SECRET_KEY=troque-isso-em-producao
# URL base para geração dos links encurtados
BASE_URL=http://localhost:8000
# Comprimento do código gerado (padrão: 6)
CODE_LENGTH=6Nunca faça commit do arquivo
.env. Ele já está no.gitignore.
feito com FastAPI + PostgreSQL + Docker + GitHub Actions + Render