Tutorial PostgreSQL Docker - ES2-UFPI/TermoLOG GitHub Wiki
Tutorial: PostgreSQL + Docker no TermoLOG
Tutorial em vídeo: https://youtu.be/kBD01n0QrTc
Guia básico para qualquer integrante da equipe começar a usar PostgreSQL no projeto. Cobre instalação, um exemplo funcional inicial, persistência (local e no servidor) e a integração com o backend FastAPI.
1. Preparação do ambiente
O projeto usa PostgreSQL containerizado via Docker — não é necessário instalar o Postgres diretamente na máquina.
1.1 Pré-requisitos
- Docker e Docker Compose instalados ([guia oficial](https://docs.docker.com/get-docker/))
- Um cliente SQL para inspecionar o banco visualmente: recomendamos o DBeaver ([download](https://dbeaver.io/download/)) ou a extensão PostgreSQL do VS Code
1.2 Subindo o banco localmente
Crie (ou use) o docker-compose.yml do projeto com um serviço Postgres:
services:
db:
image: postgres:16
restart: always
environment:
POSTGRES_USER: termolog
POSTGRES_PASSWORD: termolog
POSTGRES_DB: termolog
ports:
- "5432:5432"
volumes:
- pgdata:/var/lib/postgresql/data
volumes:
pgdata:
Suba o serviço:
docker compose up -d db
Isso cria um banco Postgres acessível em localhost:5432, com usuário termolog, senha termolog e banco termolog. O volume pgdata garante que os dados sobrevivem a um docker compose down (mas não a um down -v).
1.3 Conectando
Via cliente gráfico (DBeaver): crie uma conexão nova → PostgreSQL → host localhost, porta 5432, banco termolog, usuário/senha termolog.
Via terminal, usando o próprio cliente psql dentro do container (sem precisar instalar nada localmente):
docker compose exec db psql -U termolog -d termolog
2. Exemplo básico
Em vez de um SELECT 1, vamos criar uma tabela mínima e já fazer algo útil: registrar salas monitoradas.
-- cria a tabela
CREATE TABLE sala (
id SERIAL PRIMARY KEY,
nome TEXT NOT NULL,
temp_min NUMERIC(4,1) NOT NULL,
temp_max NUMERIC(4,1) NOT NULL
);
-- insere dados
INSERT INTO sala (nome, temp_min, temp_max)
VALUES ('Sala de manipulação', 15.0, 25.0),
('Câmara fria', -5.0, 5.0);
-- consulta
SELECT * FROM sala;
Rode isso dentro do psql (Seção 1.3) ou pelo editor SQL do DBeaver. Você deve ver as duas salas cadastradas com um id gerado automaticamente pelo SERIAL.
Conceitos usados aqui: tipos de coluna (TEXT, NUMERIC), chave primária autoincremento (SERIAL PRIMARY KEY) e os três comandos mais comuns do dia a dia (CREATE TABLE, INSERT, SELECT).
3. Persistência local (dentro do próprio Postgres)
O TermoLOG guarda leituras de temperatura/umidade associadas a uma sala e a um nó de borda. Isso é feito com uma segunda tabela e uma chave estrangeira, o mecanismo central de persistência relacional:
CREATE TABLE leitura (
id SERIAL PRIMARY KEY,
sala_id INTEGER NOT NULL REFERENCES sala(id),
temperatura NUMERIC(4,1) NOT NULL,
umidade NUMERIC(4,1) NOT NULL,
registrado_em TIMESTAMPTZ NOT NULL DEFAULT now()
);
INSERT INTO leitura (sala_id, temperatura, umidade)
VALUES (1, 23.4, 58.1);
-- consulta juntando as duas tabelas (JOIN)
SELECT sala.nome, leitura.temperatura, leitura.umidade, leitura.registrado_em
FROM leitura
JOIN sala ON sala.id = leitura.sala_id
ORDER BY leitura.registrado_em DESC;
Pontos importantes:
REFERENCES sala(id)garante que não é possível inserir uma leitura para uma sala que não existe (integridade referencial).TIMESTAMPTZ DEFAULT now()registra automaticamente o instante da leitura, sem o cliente precisar enviar isso.- O
JOINé como se combina dados de duas tabelas relacionadas — é a base de praticamente toda consulta útil em um banco relacional.
Essa mesma estrutura (sala + leitura) é o núcleo do schema do servidor do TermoLOG: cada leitura recebida de um nó é persistida assim.
4. Comunicação com o servidor (FastAPI + PostgreSQL)
No TermoLOG, quem fala com o Postgres não é o front-end nem o nó de borda diretamente — é o backend FastAPI. O fluxo é: nó → HTTPS → FastAPI → Postgres.
4.1 Dependências
pip install fastapi uvicorn sqlalchemy psycopg
4.2 Exemplo mínimo de API que grava e lê leituras
# main.py
from fastapi import FastAPI
from sqlalchemy import create_engine, text
app = FastAPI()
engine = create_engine("postgresql://termolog:termolog@localhost:5432/termolog")
@app.post("/leituras")
def registrar_leitura(sala_id: int, temperatura: float, umidade: float):
with engine.begin() as conn:
conn.execute(
text("INSERT INTO leitura (sala_id, temperatura, umidade) VALUES (:s, :t, :u)"),
{"s": sala_id, "t": temperatura, "u": umidade},
)
return {"status": "ok"}
@app.get("/salas/{sala_id}/leituras")
def listar_leituras(sala_id: int):
with engine.connect() as conn:
rows = conn.execute(
text("SELECT temperatura, umidade, registrado_em FROM leitura WHERE sala_id = :s ORDER BY registrado_em DESC"),
{"s": sala_id},
).mappings().all()
return list(rows)
Rodando com uvicorn main:app --reload, um POST em /leituras grava uma leitura no Postgres, e um GET em /salas/1/leituras devolve o histórico daquela sala — o mesmo fluxo que o nó central dedicado vai usar para enviar dados via HTTPS.
5. Exemplo de tecnologia específica: séries temporais com TIMESTAMPTZ e agregações
Como o TermoLOG lida com séries de temperatura/umidade ao longo do tempo, um recurso específico do Postgres que vale dominar cedo é a agregação por intervalo de tempo, usando date_trunc:
-- média de temperatura por hora, na sala 1
SELECT date_trunc('hour', registrado_em) AS hora, AVG(temperatura) AS temp_media
FROM leitura
WHERE sala_id = 1
GROUP BY hora
ORDER BY hora;