# AGENTS.md - PG-API PostgreSQL REST Driver
> Guia para agentes de IA trabalharem no projeto PG-API.
> Idioma: Português (conforme padrão do projeto)
---
## ⚠️ PROTOCOLO OBRIGATÓRIO: Ciclo de Desenvolvimento
**EM TODA SOLICITAÇÃO DE DESENVOLVIMENTO**, siga o ciclo definido em `.claude/skills/code/SKILL.md`:
```
1. LEVANTAMENTO → Análise do contexto e código existente
2. PLANEJAMENTO → Criar plano técnico detalhado
3. APRESENTAÇÃO → Mostrar plano com 4 opções ao usuário
4. EXECUÇÃO → Após aprovação, executar em modo /YOLO
5. CODE REVIEW → Revisão automática e ajustes
6. RESUMO → Apresentar resultado e instruções de teste
```
**⛔ NUNCA execute código diretamente sem passar pelo planejamento e aprovação.**
---
## 1. Visão Geral do Projeto
**PG-API** é um driver PostgreSQL via REST API construído em Rust. Fornece endpoints HTTP para executar queries, transações e gerenciar bancos de dados PostgreSQL com autenticação via API keys, rate limiting e connection pooling.
### Funcionalidades Principais
- **Query Execution**: Endpoints REST para queries SQL (`/v1/query`, `/v1/batch`, `/v1/transaction`)
- **Database Management**: Criar/listar/dropar bancos de dados e visualizar schema
- **Autenticação**: API keys com controle de acesso por conta
- **Rate Limiting**: Limitação de requisições por conta (sliding window)
- **Connection Pooling**: Pool de conexões PostgreSQL via Deadpool
- **Observabilidade**: Métricas opcionais para OpenSearch
---
## 2. Stack Tecnológico
| Componente | Tecnologia | Versão |
|------------|------------|--------|
| **Backend** | Rust | Edition 2024 |
| **Web Framework** | Axum | 0.8.4 |
| **Async Runtime** | Tokio | 1.x |
| **PostgreSQL Driver** | tokio-postgres | 0.7 |
| **Connection Pool** | deadpool-postgres | 0.14 |
| **Serialization** | serde | 1.x |
| **Logging** | tracing | 0.1 |
| **CORS** | tower-http | 0.6 |
### Dependências Principais (Cargo.toml)
```toml
axum = "0.8.4"
tokio = { version = "1", features = ["full"] }
tokio-postgres = { version = "0.7", features = ["with-chrono-0_4", "with-serde_json-1", "with-uuid-1"] }
deadpool-postgres = "0.14"
serde = { version = "1", features = ["derive"] }
serde_json = "1"
chrono = { version = "0.4", features = ["serde"] }
uuid = { version = "1", features = ["v4", "serde"] }
tracing = "0.1"
tracing-subscriber = { version = "0.3", features = ["env-filter", "json"] }
tower-http = { version = "0.6", features = ["cors"] }
```
---
## 3. Estrutura de Diretórios
```
/opt/dev/pg-api/
├── src/ # Código fonte Rust
│ ├── main.rs # Entry point - configuração do servidor Axum
│ ├── handlers.rs # HTTP handlers (endpoints da API)
│ ├── auth.rs # Middleware de autenticação (API keys)
│ ├── models.rs # Structs de dados (AppState, Account, QueryRequest, etc)
│ ├── database.rs # Execução de queries e gerenciamento de pools
│ ├── pool.rs # Configuração de connection pools
│ ├── rate_limit.rs # Rate limiting (sliding window)
│ ├── connection_limit.rs # Limitação de conexões por conta
│ ├── config.rs # Carregamento de configuração JSON
│ ├── setup.rs # Script de setup inicial
│ ├── workers.rs # Workers para tarefas em background
│ ├── queue.rs # Fila de tarefas
│ ├── middleware_queue.rs # Middleware de fila
│ ├── introspection.rs # Introspecção de schema PostgreSQL
│ ├── observability.rs # Métricas e logs para OpenSearch
│ └── error.rs # Tipos de erro customizados
├── config/ # Configurações JSON
│ ├── accounts.json # Contas e API keys
│ └── server.json # Configuração do servidor
├── docs/ # Documentação
│ └── OBSERVABILITY.md # Guia de observabilidade
├── scripts/ # Scripts utilitários
│ ├── generate_opensearch_token.py
│ └── manage_api_clients.py
├── migrations/ # Migrações SQL (se houver)
├── Cargo.toml # Configuração do projeto
├── Cargo.lock # Lock de dependências
├── CHANGELOG.md # Histórico de versões
├── LICENSE.md # Licença MIT
├── roadmap.md # Roadmap de desenvolvimento
└── AGENTS.md # Este arquivo
```
---
## 4. Comandos Essenciais
### Build e Desenvolvimento
```bash
# Build de desenvolvimento
cargo build
# Build de produção (otimizado)
cargo build --release
# Verificação rápida (sem gerar binário)
cargo check
# Lint com Clippy (obrigatório antes de commits)
cargo clippy -- -D warnings
# Formatar código
cargo fmt
# Verificar formatação
cargo fmt --check
```
### Testes
```bash
# Rodar todos os testes
cargo test
# Rodar teste específico
cargo test <nome_do_teste>
# Rodar com output detalhado
cargo test -- --nocapture
```
### Execução
```bash
# Executar o servidor
cargo run
# Executar em modo setup
cargo run -- setup
# Executar build de release
./target/release/pg-api
```
---
## 5. Regras Críticas de Desenvolvimento
### ⛔ PROIBIDO
**NUNCA commitar com warnings** | `cargo check` e `cargo clippy` devem passar limpo |
| **NUNCA usar `unwrap()` em produção** | Usar `?` ou tratamento adequado de erros |
| **NUNCA expor credenciais** | Não commitar arquivos de config com senhas |
| **NUNCA ignorar erros de teste** | Todos os testes devem passar |
### ✅ OBRIGATÓRIO
| **Tratamento de erros adequado** | Usar `thiserror` para erros definidos, `anyhow` para genéricos |
| **Logs estruturados** | Usar `tracing::info!`, `tracing::error!`, etc |
| **Validação de inputs** | Validar todos os dados de entrada |
| **Documentar funções públicas** | Usar `///` para docstrings |
---
## 6. Padrões de Código
### Estrutura de Handlers
```rust
use axum::{
extract::{Json, State},
http::StatusCode,
response::IntoResponse,
};
use serde_json::json;
use crate::{models::*, database::*};
/// Handler para executar uma query SQL
pub async fn query_handler(
State(state): State<AppState>,
Extension(account): Extension<Account>,
Json(request): Json<QueryRequest>,
) -> Result<Json<ApiResponse<QueryResult>>, StatusCode> {
// 1. Validar permissões
// 2. Executar operação
// 3. Retornar resposta padronizada
}
```
### Estrutura de Models
```rust
use serde::{Deserialize, Serialize};
/// Representa uma requisição de query
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct QueryRequest {
pub database: String,
pub query: String,
pub params: Option<Vec<Value>>,
pub options: Option<QueryOptions>,
}
impl QueryRequest {
/// Valida a requisição antes da execução
pub fn validate(&self) -> Result<(), ValidationError> {
// Validação
}
}
```
### Resposta Padronizada da API
```rust
/// Estrutura padrão de resposta da API
#[derive(Debug, Serialize)]
pub struct ApiResponse<T> {
pub success: bool,
pub data: Option<T>,
pub error: Option<ApiError>,
pub metadata: ResponseMetadata,
}
impl<T> ApiResponse<T> {
pub fn success(data: T, metadata: ResponseMetadata) -> Self {
Self {
success: true,
data: Some(data),
error: None,
metadata,
}
}
pub fn error(code: &str, message: impl ToString, metadata: ResponseMetadata) -> Self {
Self {
success: false,
data: None,
error: Some(ApiError {
code: code.to_string(),
message: message.to_string(),
}),
metadata,
}
}
}
```
---
## 7. Configuração
### Arquivos de Configuração
**config/server.json**:
```json
{
"host": "127.0.0.1",
"port": 8580,
"log_level": "info"
}
```
**config/accounts.json**:
```json
{
"accounts": [
{
"id": "acc_001",
"name": "Production Account",
"api_keys": ["sk_live_xxxxxxxx"],
"role": "owner",
"rate_limit": 1000,
"max_connections": 50,
"databases": [
{
"database": "mydb",
"username": "api_user",
"password": "secret",
"permissions": ["SELECT", "INSERT", "UPDATE"]
}
]
}
]
}
```
### Variáveis de Ambiente
```bash
# Configuração do servidor
APP__ADDR=127.0.0.1:8580
APP__LOG_LEVEL=info
# PostgreSQL
PG__MAX_CONNECTIONS=100
PG__POOL_SIZE=25
# Observabilidade (opcional)
OPENSEARCH_ENABLED=false
OPENSEARCH_API_URL=https://opensearch.example.com
OPENSEARCH_API_TOKEN=sk_live_xxxxx
```
---
## 8. Endpoints da API
| GET | `/health` | Health check | Público |
| GET | `/v1/status` | Status do serviço | API Key |
| POST | `/v1/query` | Executar query SQL | API Key |
| POST | `/v1/batch` | Executar batch de queries | API Key |
| POST | `/v1/transaction` | Executar transação | API Key |
| GET | `/v1/databases` | Listar databases | API Key |
| POST | `/v1/databases` | Criar database | API Key |
| DELETE | `/v1/databases/{name}` | Dropar database | API Key |
| GET | `/v1/databases/{db}/tables` | Listar tabelas | API Key |
| GET | `/v1/databases/{db}/schema` | Ver schema | API Key |
| GET | `/v1/account` | Info da conta | API Key |
| GET | `/v1/account/usage` | Estatísticas de uso | API Key |
| GET | `/docs` | Documentação Swagger | Público |
| GET | `/openapi.json` | Especificação OpenAPI | Público |
---
## 9. Middleware Stack (Ordem de Execução)
```rust
// Ordem de aplicação (de fora para dentro):
1. CORS (CorsLayer::permissive())
2. Request ID (auth::request_id_middleware)
3. Autenticação (auth::auth_middleware)
4. Rate Limiting (rate_limit::rate_limit_middleware)
5. Connection Limit (connection_limit::connection_limit_middleware)
6. Métricas (observability::metrics_middleware)
```
---
## 10. Testes
### Testes Unitários
```rust
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_query_request_validation() {
let request = QueryRequest {
database: "test".to_string(),
query: "SELECT 1".to_string(),
params: None,
options: None,
};
assert!(request.validate().is_ok());
}
}
```
### Testes de Integração
Colocar em `tests/integration_test.rs`:
```rust
use reqwest::Client;
#[tokio::test]
async fn test_health_endpoint() {
let client = Client::new();
let response = client
.get("http://localhost:8580/health")
.send()
.await
.unwrap();
assert_eq!(response.status(), 200);
}
```
---
## 11. Roadmap e Tarefas Pendentes
Consultar `roadmap.md` para o planejamento completo.
### Tarefas em Aberto (do CRUSH.md anterior):
- [ ] Implementar endpoint `/ready` (readiness checks)
- [ ] Implementar endpoint `/metrics` (Prometheus)
- [ ] Token rotation mechanism
- [ ] Request signing (HMAC)
- [ ] Grafana dashboard templates
- [ ] Alerting rules
---
## 12. Contatos e Recursos
- **Repositório**: https://gitlab.com/aerunti/pg-api
- **Documentação**: http://localhost:8580/docs (quando rodando)
- **OpenAPI**: http://localhost:8580/openapi.json
### Scripts Úteis
```bash
# Gerar token OpenSearch
python3 scripts/generate_opensearch_token.py --environment production
# Gerenciar clientes API
python3 scripts/manage_api_clients.py --list
```
---
> **Nota**: Este documento deve ser atualizado quando novos padrões forem estabelecidos ou mudanças arquiteturais ocorrerem.