Análise de Compatibilidade de Tecnologias — SafeStreets
Visão geral da stack
| Camada | Tecnologia |
|---|---|
| Front-end | Next.js + TypeScript + CSS Modules |
| Back-end | Python + FastAPI |
| Banco de dados | PostgreSQL |
| ORM | SQLAlchemy |
| Containerização | Docker |
| IA | Gemini API |
| Geocodificação | Nominatim |
| Mapas | Leaflet.js |
| Fontes de dados | Feeds RSS (Metrópoles, Correio Braziliense) |
| Documentação da API | Swagger / OpenAPI |
Resultado geral
A stack é compatível. Todas as tecnologias escolhidas se comunicam por padrões abertos (HTTP/REST e JSON), o que elimina dependências diretas entre front-end e back-end. Os pontos de atenção existem, mas todos têm solução clara descrita neste documento.
Análise por par de tecnologias
Next.js ↔ FastAPI
Compatibilidade: total.
Next.js consome qualquer API REST via fetch nativo ou bibliotecas como axios. O FastAPI expõe endpoints REST com retorno em JSON, que é o formato esperado pelo front-end. A comunicação segue o padrão cliente-servidor sem nenhuma dependência de linguagem ou framework entre os dois lados.
Ponto de atenção — CORS:
Por padrão, o FastAPI bloqueia requisições vindas de origens diferentes (como localhost:3000 do Next.js acessando localhost:8000 do FastAPI). Isso precisa ser configurado explicitamente.
Solução:
# main.py — FastAPI
from fastapi.middleware.cors import CORSMiddleware
app.add_middleware(
CORSMiddleware,
allow_origins=["http://localhost:3000"], # em produção, coloque o domínio real
allow_methods=["*"],
allow_headers=["*"],
)
Leaflet.js ↔ Next.js (SSR)
Compatibilidade: requer configuração.
Este é o conflito mais comum nessa stack. O Leaflet.js acessa o objeto window do navegador durante a inicialização. O Next.js, por padrão, tenta renderizar componentes no servidor (SSR), onde window não existe — isso causa o erro window is not defined em tempo de build ou execução.
Solução — carregar o componente de mapa apenas no cliente:
// components/Mapa.tsx
import dynamic from "next/dynamic";
const MapaLeaflet = dynamic(() => import("./MapaLeaflet"), {
ssr: false, // desativa a renderização no servidor para este componente
loading: () => <p>Carregando mapa...</p>,
});
export default MapaLeaflet;
O componente MapaLeaflet interno pode usar o Leaflet normalmente, pois só será executado no navegador.
Nominatim ↔ FastAPI
Compatibilidade: total, com restrição de uso.
O Nominatim é o serviço de geocodificação do OpenStreetMap. A chamada deve ser feita sempre pelo back-end (FastAPI), nunca diretamente pelo front-end. Isso evita expor a origem das requisições e centraliza o controle de cache e rate limit.
Ponto de atenção — limite de requisições:
O servidor público do Nominatim (nominatim.openstreetmap.org) impõe um limite de 1 requisição por segundo e exige um User-Agent identificando o projeto. Exceder esse limite resulta em bloqueio de IP.
Solução — wrapper com cache no FastAPI:
import httpx
from functools import lru_cache
NOMINATIM_URL = "https://nominatim.openstreetmap.org/search"
HEADERS = {"User-Agent": "SafeStreets/1.0 (contato@safestreets.com)"}
@lru_cache(maxsize=512)
def geocodificar(endereco: str) -> dict | None:
"""
Converte um endereço em coordenadas.
O cache evita chamadas repetidas para o mesmo endereço.
"""
params = {"q": endereco, "format": "json", "limit": 1}
response = httpx.get(NOMINATIM_URL, params=params, headers=HEADERS)
resultados = response.json()
if resultados:
return {
"lat": float(resultados[0]["lat"]),
"lon": float(resultados[0]["lon"]),
"display_name": resultados[0]["display_name"],
}
return None
Se o volume de geocodificações crescer muito, considere instalar uma instância própria do Nominatim via Docker — a imagem oficial está disponível em mediagis/nominatim.
Gemini API ↔ FastAPI
Compatibilidade: total.
A biblioteca google-generativeai é Python nativa, integrando diretamente com o FastAPI sem nenhuma adaptação. O Gemini será chamado pelo back-end para processar o texto das notícias RSS e extrair localidade e tipo de ocorrência.
Ponto de atenção — chamadas assíncronas:
O FastAPI é assíncrono por natureza (async/await). A biblioteca padrão do Gemini tem suporte a chamadas síncronas e assíncronas. Use a versão assíncrona para não bloquear o event loop do FastAPI.
Solução:
import google.generativeai as genai
import asyncio
genai.configure(api_key="SUA_CHAVE")
model = genai.GenerativeModel("gemini-1.5-flash")
async def extrair_localidade(texto: str) -> dict:
loop = asyncio.get_event_loop()
# executa a chamada síncrona em uma thread separada para não bloquear
resposta = await loop.run_in_executor(
None,
lambda: model.generate_content(texto)
)
return resposta.text
Feeds RSS ↔ FastAPI
Compatibilidade: total.
O feedparser é uma biblioteca Python que lê feeds RSS e retorna os dados como dicionário Python — integração direta com o FastAPI. O RSS não tem autenticação, não tem SDK, é apenas uma requisição HTTP GET para uma URL fixa.
Ponto de atenção — coleta periódica: O FastAPI não tem um agendador de tarefas nativo. Para coletar os feeds RSS automaticamente a cada intervalo de tempo (por exemplo, a cada hora), você precisaria de uma solução de agendamento separada.
Solução — usar APScheduler junto com o FastAPI:
from apscheduler.schedulers.asyncio import AsyncIOScheduler
from contextlib import asynccontextmanager
from fastapi import FastAPI
scheduler = AsyncIOScheduler()
async def coletar_rss():
"""Função que será executada periodicamente."""
# lógica de coleta e processamento dos feeds
pass
@asynccontextmanager
async def lifespan(app: FastAPI):
scheduler.add_job(coletar_rss, "interval", hours=1)
scheduler.start()
yield
scheduler.shutdown()
app = FastAPI(lifespan=lifespan)
Isso garante que a coleta acontece automaticamente sem precisar de um serviço externo.
Feeds RSS ↔ Gemini API
Compatibilidade: total — integração intencional.
Esses dois componentes formam o núcleo de inteligência do SafeStreets. O RSS entrega texto bruto (título e resumo da notícia) e o Gemini extrai estrutura (localidade, tipo de ocorrência, bairro). O resultado estruturado é então passado para o Nominatim para geocodificação.
Fluxo completo:
Feed RSS → texto da notícia
→ Gemini API → {"local": "QNL 8, Taguatinga", "tipo": "roubo"}
→ Nominatim → {"lat": -15.8397, "lon": -48.0536}
→ PostgreSQL → registro salvo com coordenadas
→ FastAPI → endpoint REST
→ Next.js → Leaflet.js renderiza no mapa
PostgreSQL ↔ SQLAlchemy ↔ FastAPI
Compatibilidade: total — combinação padrão da indústria.
Essa tríade é amplamente utilizada e bem documentada. O SQLAlchemy gerencia a comunicação com o PostgreSQL e o FastAPI usa os modelos do SQLAlchemy para validar e persistir dados.
Ponto de atenção — driver assíncrono:
Para aproveitar o modelo assíncrono do FastAPI, o driver de conexão com o PostgreSQL precisa ser o asyncpg, não o psycopg2 padrão.
Dependências necessárias:
fastapi
sqlalchemy[asyncio]
asyncpg
Configuração da conexão:
from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession
DATABASE_URL = "postgresql+asyncpg://usuario:senha@localhost/safestreets"
engine = create_async_engine(DATABASE_URL)
Docker ↔ stack completa
Compatibilidade: total.
O Docker isola cada serviço em um container separado, eliminando conflitos de dependência entre o front-end (Node.js) e o back-end (Python). A comunicação entre containers é feita via rede interna do Docker Compose.
Estrutura recomendada de containers:
# docker-compose.yml
services:
frontend:
build: ./frontend
ports:
- "3000:3000"
backend:
build: ./backend
ports:
- "8000:8000"
depends_on:
- db
environment:
- DATABASE_URL=postgresql+asyncpg://postgres:senha@db/safestreets
- GEMINI_API_KEY=${GEMINI_API_KEY}
db:
image: postgres:16
environment:
- POSTGRES_PASSWORD=senha
- POSTGRES_DB=safestreets
volumes:
- postgres_data:/var/lib/postgresql/data
volumes:
postgres_data:
Ponto de atenção — variáveis de ambiente sensíveis:
A chave da Gemini API e credenciais do banco nunca devem estar no código ou no docker-compose.yml diretamente. Use um arquivo .env na raiz do projeto e adicione-o ao .gitignore.
TypeScript ↔ Swagger / OpenAPI
Compatibilidade: total — integração altamente recomendada.
O FastAPI gera automaticamente um schema OpenAPI em /openapi.json. Esse schema pode ser usado para gerar automaticamente os tipos TypeScript do front-end, eliminando erros de integração entre front e back.
Ferramenta recomendada — openapi-typescript:
npx openapi-typescript http://localhost:8000/openapi.json -o src/types/api.d.ts
Isso gera um arquivo com todos os tipos da API prontos para uso no Next.js, garantindo que o front-end e o back-end estejam sempre sincronizados.
Resumo dos pontos de atenção
| Conflito | Gravidade | Solução |
|---|---|---|
| Leaflet.js + SSR do Next.js | Alta — quebra o build | dynamic import com ssr: false |
| CORS entre Next.js e FastAPI | Alta — bloqueia requisições | Middleware CORS no FastAPI |
| Nominatim rate limit (1 req/s) | Média — risco de bloqueio de IP | Cache com lru_cache no back-end |
| Gemini API bloqueando o event loop | Média — degrada performance | run_in_executor para chamadas síncronas |
| Agendamento da coleta RSS | Média — sem agendador nativo no FastAPI | APScheduler integrado ao lifespan |
| Driver PostgreSQL síncrono | Baixa — perde performance assíncrona | Substituir psycopg2 por asyncpg |
| Chaves de API no código | Alta — risco de segurança | Arquivo .env + .gitignore |
Dependências Python necessárias
fastapi
uvicorn[standard]
sqlalchemy[asyncio]
asyncpg
feedparser
google-generativeai
httpx
apscheduler
python-dotenv
Dependências Node.js necessárias
next
react
react-dom
typescript
leaflet
react-leaflet
@types/leaflet
axios
openapi-typescript
Conclusão — Análise de Compatibilidade SafeStreets
A análise de compatibilidade da stack do SafeStreets demonstra que o conjunto de tecnologias escolhido é coeso, moderno e tecnicamente viável. Todas as camadas do sistema se comunicam por meio de padrões abertos e amplamente consolidados — HTTP, REST e JSON — o que elimina dependências diretas entre front-end e back-end e garante que cada parte do projeto possa evoluir de forma independente.
Nenhum dos conflitos identificados representa um impeditivo ao desenvolvimento. Os pontos de atenção mapeados — como a incompatibilidade do Leaflet.js com o SSR do Next.js, a configuração de CORS entre os serviços e o limite de requisições do Nominatim — são problemas conhecidos, com soluções documentadas e de baixa complexidade de implementação. Desde que tratados no início do desenvolvimento, não impactarão a arquitetura nem a escalabilidade do projeto.
A escolha do Python com FastAPI como base do back-end se mostrou especialmente acertada para o SafeStreets, pois viabiliza de forma natural a integração com a API do Gemini para processamento inteligente das notícias coletadas via RSS, com o Nominatim para geocodificação e com o PostgreSQL para persistência — formando uma cadeia de dados coerente desde a coleta até a exibição no mapa.
A adoção do Docker como ambiente de containerização reforça a compatibilidade geral do projeto, isolando as dependências de cada serviço e garantindo que a stack funcione de forma consistente em qualquer ambiente, do desenvolvimento à produção.
Em síntese, o SafeStreets foi projetado sobre uma base tecnológica sólida, sem sobreposições desnecessárias e sem lacunas críticas de integração. As tecnologias escolhidas se complementam e estão alinhadas com as necessidades funcionais do sistema, oferecendo boas condições para o desenvolvimento, manutenção e crescimento do projeto.