| name | postman-development |
| description | Skill completa para desenvolvimento e documentação de APIs usando Postman, incluindo automação, CI/CD, e geração a partir de OpenAPI. |
| triggers | ["postman","api documentation","newman","collection","openapi","jwt","session auth","api testing"] |
Postman Development - Skill Completa
Skill completa para desenvolvimento, documentação e automação de APIs usando Postman, seguindo padrões de produção e melhores práticas de mercado.
Quando Usar
Aplicar esta skill quando:
- Documentando APIs REST (Express, Django DRF, FastAPI)
- Criando collections Postman para testes
- Configurando automação com Newman
- Integrando testes Postman em CI/CD
- Gerando collections a partir de OpenAPI
- Implementando autenticação JWT ou Session
Estrutura de Collection
Informações Básicas (info)
{
"info": {
"_postman_id": "unique-id",
"name": "Nome da API - Documentação",
"description": "# Descrição Completa\n\n## Visão Geral\n...",
"schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
}
}
Requisitos:
- Nome descritivo e claro
- Description em Markdown com visão geral completa
- Listar todos os serviços/módulos incluídos
- URLs base de cada serviço
- Informações sobre autenticação
- Status dos serviços (se aplicável)
Organização Hierárquica
Collection/
├── Authentication/
│ ├── Login
│ ├── Refresh Token
│ └── Validate Token
├── [Módulo Principal]/
│ ├── [Submódulo]/
│ │ ├── List [Resource]
│ │ ├── Create [Resource]
│ │ ├── Get [Resource]
│ │ ├── Update [Resource]
│ │ └── Delete [Resource]
│ └── [Outro Submódulo]/
└── Dashboard/
├── Filters
├── Stats
└── Charts/
Padrões de Nomenclatura:
- Pastas: PascalCase (ex:
Authentication, Financial KPIs)
- Endpoints: Verb + Resource (ex:
List Contracts, Create Contract)
- Subpastas: Descritivas e hierárquicas
Variáveis de Ambiente
Variáveis Obrigatórias
{
"id": "postman-env-default",
"name": "Local Env",
"values": [
{ "key": "base_url", "value": "http://localhost:8000", "enabled": true },
{ "key": "auth_token", "value": "", "enabled": true },
{ "key": "refresh_token", "value": "", "enabled": true },
{ "key": "auth_expires_at", "value": "", "enabled": true },
{ "key": "user_email", "value": "test@example.com", "enabled": true },
{ "key": "user_password", "value": "changeme", "enabled": true }
]
}
Variáveis padrão:
base_url: URL base da aplicação
auth_token: Token JWT (preenchido automaticamente)
refresh_token: Refresh token (preenchido automaticamente)
auth_expires_at: Timestamp de expiração do token
user_email: Email do usuário de teste
user_password: Senha do usuário de teste
Para Django DRF:
admin_username: Username do admin
admin_password: Password do admin
encoded_token: Token codificado (alternativa)
Pre-request Scripts
Collection-Level: Autenticação JWT Automática
Snippet obrigatório para todas as collections com JWT:
if (pm.environment.get('auth_token')) {
pm.request.headers.upsert({
key: 'Authorization',
value: 'Bearer ' + pm.environment.get('auth_token')
});
}
let expiresAt = pm.environment.get('auth_expires_at');
if (expiresAt && Date.now() >= parseInt(expiresAt)) {
console.log('Access token expired — attempting refresh...');
let refreshReq = {
url: pm.environment.get('base_url') + '/auth/refresh',
method: 'POST',
header: { 'Content-Type': 'application/json' },
body: {
mode: 'raw',
raw: JSON.stringify({
refresh_token: pm.environment.get('refresh_token')
})
}
};
pm.sendRequest(refreshReq, function (err, res) {
if (!err && res.code === 200) {
let json = res.json();
if (json.access_token) {
pm.environment.set('auth_token', json.access_token);
}
if (json.refresh_token) {
pm.environment.set('refresh_token', json.refresh_token);
}
if (json.expires_in) {
pm.environment.set('auth_expires_at', Date.now() + (json.expires_in * 1000));
}
} else {
console.error('Refresh failed', err || res);
}
});
}
Folder-Level: Autenticação Automática (Fallback)
Para endpoints que requerem autenticação mas não têm token:
if (!pm.environment.get("auth_token")) {
pm.sendRequest({
url: pm.environment.get("base_url") + "/auth/login",
method: "POST",
header: {
"Content-Type": "application/json"
},
body: {
mode: "raw",
raw: JSON.stringify({
email: pm.environment.get("user_email"),
password: pm.environment.get("user_password")
})
}
}, function (err, res) {
if (res.code === 200) {
const jsonData = res.json();
if (jsonData.access_token) {
pm.environment.set("auth_token", jsonData.access_token);
}
if (jsonData.refresh_token) {
pm.environment.set("refresh_token", jsonData.refresh_token);
}
if (jsonData.expires_in) {
pm.environment.set("auth_expires_at", Date.now() + (jsonData.expires_in * 1000));
}
}
});
}
Variáveis Dinâmicas
pm.environment.set("current_timestamp", new Date().toISOString());
const futureDate = new Date();
futureDate.setDate(futureDate.getDate() + 30);
pm.environment.set("future_date", futureDate.toISOString().split('T')[0]);
Test Scripts
Login: Salvar Tokens Automaticamente
Snippet obrigatório para endpoint /auth/login:
pm.test("Login successful", function () {
pm.response.to.have.status(200);
});
let res = pm.response.json();
if (res.access_token) {
pm.environment.set('auth_token', res.access_token);
}
if (res.refresh_token) {
pm.environment.set('refresh_token', res.refresh_token);
}
if (res.expires_in) {
let expiresAt = Date.now() + (res.expires_in * 1000);
pm.environment.set('auth_expires_at', expiresAt);
}
Validação de Status Code
pm.test("Status code is 200", function () {
pm.response.to.have.status(200);
});
Validação de Schema
pm.test("Response has required fields", function () {
const jsonData = pm.response.json();
pm.expect(jsonData).to.have.property("id");
pm.expect(jsonData).to.have.property("name");
});
Validação de Tipos
pm.test("Response data types are correct", function () {
const jsonData = pm.response.json();
pm.expect(jsonData.id).to.be.a("string");
pm.expect(jsonData.count).to.be.a("number");
});
Salvar Variáveis da Resposta
if (pm.response.code === 200) {
const jsonData = pm.response.json();
pm.environment.set("resource_id", jsonData.id);
pm.environment.set("logged_user", jsonData.user.id);
}
Descrições de Endpoints
Estrutura Obrigatória
Cada endpoint DEVE ter descrição completa em Markdown:
## [Nome do Endpoint]
### Validações
- campo (tipo): Descrição → status_code: "mensagem de erro"
- campo (tipo): Descrição → status_code: "mensagem de erro"
### Regras de Negócio
- Regra 1
- Regra 2
- Regra 3
### Erros
- status_code: Descrição do erro
- status_code: Descrição do erro
Exemplo:
## Login
### Validações
- email (string): Obrigatório → 422: "Email is required"
- password (string): Obrigatório → 422: "Password is required"
### Regras de Negócio
- Credenciais devem estar corretas
- Usuário deve estar ativo
- Token válido por 24h (access_token)
- Refresh token válido por 7 dias
### Erros
- 422: Email/Password ausente
- 401: Credenciais inválidas
- 403: Usuário inativo
Exemplos de Resposta
Estrutura Obrigatória
Cada endpoint DEVE ter:
-
Exemplo de Sucesso (status 200/201)
- Nome: "✅ Sucesso" ou "✅ Sucesso - [Descrição]"
- Body completo e real
- Headers relevantes
-
Exemplos de Erro de Validação (status 400/422)
- Um exemplo para cada campo obrigatório ausente
- Um exemplo para cada validação específica
- Mensagens de erro exatas
-
Exemplos de Erro de Negócio (status 404/403/401)
- Recurso não encontrado
- Permissão negada
- Token inválido/expirado
Formato de Exemplos
{
"name": "✅ Sucesso - Contrato criado",
"code": 201,
"body": "{\n \"id\": 1,\n \"term_number\": \"123/2024\",\n \"status\": \"active\"\n}"
}
{
"name": "❌ 422 - term_number ausente",
"code": 422,
"body": "{\n \"detail\": [\n {\n \"type\": \"value_error.missing\",\n \"loc\": [\"body\", \"term_number\"],\n \"msg\": \"This field is required.\"\n }\n ]\n}"
}
Documentação de Parâmetros
Query Parameters
{
"key": "year",
"value": "2024",
"description": "Ano para filtrar os dados (formato: YYYY).",
"disabled": true
}
Requisitos:
- Descrição clara do parâmetro
- Tipo de dado esperado
- Formato (se aplicável)
- Exemplo de valor
- Se é obrigatório ou opcional
Path Parameters
{
"variable": [
{
"key": "id",
"value": "1",
"description": "ID do contrato (número inteiro)."
}
]
}
Body Parameters
Para JSON:
- Usar variáveis quando possível:
{{variable_name}}
- Incluir comentários quando necessário
- Exemplos com dados realistas
Para multipart/form-data:
- Documentar cada campo
- Tipo de arquivo aceito (se aplicável)
- Tamanho máximo (se aplicável)
Choices para Campos Enum
### Campo: status
**Tipo:** ChoiceField
**Valores aceitos:**
- `PENDING`: Pendente
- `PROCESSING`: Processando
- `COMPLETED`: Concluído
- `FAILED`: Falha
Autenticação
JWT (JSON Web Tokens)
Endpoints típicos:
POST /auth/login → retorna { access_token, refresh_token, expires_in }
POST /auth/refresh → retorna novo access_token (rotacionar refresh token)
POST /auth/logout → invalidar refresh token (DB ou blacklist)
Configuração recomendada:
access_token: expiração curta (ex: 15 minutos)
refresh_token: long-lived (ex: 7 dias)
- Auto-refresh implementado no pre-request script
Session / Cookie (Django DRF)
Para APIs que usam sessão:
- Configure Pre-request para enviar cookies
- Postman gerencia cookies automaticamente
- Tests: captura e salva
Set-Cookie se necessário
pm.request.headers.upsert({
key: 'X-CSRFToken',
value: pm.cookies.get('csrftoken')
});
Automação com Newman
O que é o Newman?
Newman é o CLI (Command Line Interface) do Postman. Ele NÃO converte documentação em collection. Ele executa uma collection Postman já existente.
Diferença importante:
- ❌ NÃO faz: Converter Swagger/OpenAPI/ReDoc → Postman Collection
- ✅ FAZ: Executar testes de uma collection Postman já criada
Script de Execução Local
Arquivo: scripts/run_newman.sh
Este script executa uma collection Postman usando o Newman:
#!/usr/bin/env bash
COLLECTION=${1:-postman/collection.json}
ENV=${2:-postman/environment.json}
REPORT_DIR=${REPORT_DIR:-reports/postman}
mkdir -p "$REPORT_DIR"
npx newman run "$COLLECTION" -e "$ENV" \
--reporters cli,junit,html \
--reporter-junit-export "$REPORT_DIR/junit-results.xml" \
--reporter-html-export "$REPORT_DIR/report.html"
EXIT_CODE=$?
if [ $EXIT_CODE -ne 0 ]; then
echo "Newman tests failed (exit $EXIT_CODE)"
exit $EXIT_CODE
fi
echo "Newman finished."
Uso
./scripts/run_newman.sh
./scripts/run_newman.sh postman/collection.json postman/environment.json
O que acontece:
- Newman lê a collection Postman (
collection.json)
- Executa todos os requests da collection
- Roda os test scripts de cada request
- Gera relatórios (CLI, JUnit XML, HTML)
- Retorna código de saída (0 = sucesso, != 0 = falha)
CI/CD - GitHub Actions
Workflow Completo
Arquivo: .github/workflows/postman-ci.yml
name: Postman Collection Tests
on:
push:
branches: [ main, master ]
pull_request:
jobs:
postman-tests:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: '18'
- name: Install dependencies
run: npm ci || npm i
- name: Install jq (for environment manipulation)
run: sudo apt-get update && sudo apt-get install -y jq
- name: Run Newman (Postman tests)
env:
BASE_URL: ${{ secrets.BASE_URL }}
USER_EMAIL: ${{ secrets.TEST_USER_EMAIL }}
USER_PASSWORD: ${{ secrets.TEST_USER_PASSWORD }}
run: |
# Write environment with secrets
jq '.values[] |= (if .key=="base_url" then .value="'$BASE_URL'" elif .key=="user_email" then .value="'$USER_EMAIL'" elif .key=="user_password" then .value="'$USER_PASSWORD'" else . end)' postman/environment.json > tmp_env.json
npx newman run postman/collection.json -e tmp_env.json \
--reporters cli,junit \
--reporter-junit-export postman/junit-results.xml
- name: Upload results
uses: actions/upload-artifact@v4
with:
name: postman-results
path: postman/junit-results.xml
Secrets Necessários
Configurar no GitHub Repository Settings → Secrets:
BASE_URL: URL base da API (ex: https://api.example.com)
TEST_USER_EMAIL: Email do usuário de teste
TEST_USER_PASSWORD: Senha do usuário de teste
Geração a partir de OpenAPI/Swagger
⚠️ IMPORTANTE: Diferença entre Conversão e Execução
Conversão (OpenAPI → Postman):
- Ferramenta:
openapi-to-postmanv2 ou postman-collection-transformer
- O que faz: Converte arquivo OpenAPI/Swagger em uma collection Postman
- Quando usar: Quando você tem OpenAPI e quer gerar collection automaticamente
Execução (Newman):
- Ferramenta:
newman (via run_newman.sh)
- O que faz: Executa testes de uma collection Postman já existente
- Quando usar: Para rodar testes automatizados da collection
Fluxo Completo Recomendado
1. Gerar/Atualizar OpenAPI
Express.js:
npm install swagger-jsdoc
Django DRF:
pip install drf-spectacular
FastAPI:
2. Converter OpenAPI → Postman Collection
Script: generate-from-openapi.sh
#!/usr/bin/env bash
OPENAPI_FILE=${1:-openapi.json}
OUTPUT_DIR=${2:-postman}
if ! command -v openapi-to-postmanv2 &> /dev/null; then
npm install -g openapi-to-postmanv2
fi
openapi-to-postmanv2 -s "$OPENAPI_FILE" -o "$OUTPUT_DIR/collection.json"
echo "✅ Collection gerada em $OUTPUT_DIR/collection.json"
Uso:
./generate-from-openapi.sh openapi.json postman
./generate-from-openapi.sh docs/swagger.json postman
3. Injetar Scripts Automáticos
Após a conversão, você precisa adicionar manualmente (ou via script):
- Pre-request scripts de autenticação (collection-level)
- Test scripts para salvar tokens
- Configurar variáveis de ambiente
Exemplo de script Python para injetar scripts:
import json
with open('postman/collection.json', 'r') as f:
collection = json.load(f)
collection['event'] = [{
"listen": "prerequest",
"script": {
"exec": [
"// Auto-attach Bearer token",
"if (pm.environment.get('auth_token')) {",
" pm.request.headers.upsert({ key: 'Authorization', value: 'Bearer ' + pm.environment.get('auth_token') });",
"}"
],
"type": "text/javascript"
}
}]
with open('postman/collection.json', 'w') as f:
json.dump(collection, f, indent=2)
4. Executar Testes com Newman
Agora sim, usar o run_newman.sh:
./run_newman.sh postman/collection.json postman/environment.json
5. Commit e CI
- Commitar collection no diretório
postman/
- CI executa Newman automaticamente (via GitHub Actions)
- Opcional: Publicar no Postman API via
collections.publish
Resumo do Fluxo
1. OpenAPI/Swagger (gerado pelo framework)
↓
2. openapi-to-postmanv2 (CONVERTE)
↓
3. postman/collection.json (collection gerada)
↓
4. Adicionar scripts manualmente (autenticação, testes)
↓
5. run_newman.sh (EXECUTA testes)
↓
6. Relatórios (junit-results.xml, report.html)
Templates de Collection
Template Base (JWT)
Estrutura mínima:
{
"info": {
"name": "API - Boilerplate (Auth + CRUD)",
"schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json",
"description": "Coleção padrão: Auth (JWT), exemplos CRUD."
},
"item": [
{
"name": "Auth",
"item": [
{
"name": "POST /auth/login",
"request": {
"method": "POST",
"header": [
{ "key": "Content-Type", "value": "application/json" }
],
"body": {
"mode": "raw",
"raw": "{\n \"email\": \"{{user_email}}\",\n \"password\": \"{{user_password}}\"\n}"
},
"url": {
"raw": "{{base_url}}/auth/login",
"host": ["{{base_url}}"],
"path": ["auth","login"]
}
},
"event": [
{
"listen": "test",
"script": {
"exec": [
"pm.test('status 200', function () { pm.response.to.have.status(200); });",
"let res = pm.response.json();",
"if(res.access_token){ pm.environment.set('auth_token', res.access_token); }",
"if(res.refresh_token){ pm.environment.set('refresh_token', res.refresh_token); }",
"if(res.expires_in){ let expiresAt = Date.now() + (res.expires_in * 1000); pm.environment.set('auth_expires_at', expiresAt); }"
],
"type": "text/javascript"
}
}
]
}
]
}
],
"event": [
{
"listen": "prerequest",
"script": {
"exec": [
"// Collection-level pre-request: attach Authorization header if auth_token present",
"if (pm.environment.get('auth_token')) {",
" pm.request.headers.upsert({ key: 'Authorization', value: 'Bearer ' + pm.environment.get('auth_token') });",
"}",
"",
"// Auto-refresh if token expired",
"let expiresAt = pm.environment.get('auth_expires_at');",
"if (expiresAt && Date.now() >= parseInt(expiresAt)) {",
" console.log('Access token expired — attempting refresh...');",
" let refreshReq = {",
" url: pm.environment.get('base_url') + '/auth/refresh',",
" method: 'POST',",
" header: { 'Content-Type': 'application/json' },",
" body: { mode: 'raw', raw: JSON.stringify({ refresh_token: pm.environment.get('refresh_token') }) }",
" };",
" pm.sendRequest(refreshReq, function (err, res) {",
" if (!err && res.code === 200) {",
" let json = res.json();",
" if (json.access_token) { pm.environment.set('auth_token', json.access_token); }",
" if (json.refresh_token) { pm.environment.set('refresh_token', json.refresh_token); }",
" if (json.expires_in) { pm.environment.set('auth_expires_at', Date.now() + (json.expires_in * 1000)); }",
" } else { console.error('Refresh failed', err || res); }",
" });",
"}"
],
"type": "text/javascript"
}
}
]
}
Framework-Specific
Express.js
Endpoints típicos:
POST /auth/login → { access_token, refresh_token, expires_in }
POST /auth/refresh → novo access_token (rotacionar refresh token)
POST /auth/logout → invalidar refresh token
Pacotes recomendados:
jsonwebtoken para gerar tokens
express-jwt ou middleware customizado para validação
Configuração de desenvolvimento:
expires_in curto (ex: 15 minutos) para access_token
- Refresh token long-lived para facilitar testes
Django DRF
Configuração:
- Use
djangorestframework-simplejwt ou dj-rest-auth
- Configure token lifetimes no
settings.py
- Endpoints:
/api/token/ e /api/token/refresh/
Para sessões (cookies):
- Ative CSRF
- Teste com Postman usando cookie jar
- Envie CSRF token nos headers
Variáveis de ambiente:
admin_username: Username do admin
admin_password: Password do admin
encoded_token: Token codificado (alternativa)
FastAPI
OpenAPI automático:
- FastAPI gera OpenAPI automaticamente
- Use
openapi-to-postmanv2 para converter
- Injete scripts de autenticação após conversão
Checklist de Qualidade
Estrutura
Endpoints
Exemplos
Scripts
Parâmetros
Automação
Casos Especiais
Boas Práticas
1. Máxima Automação
- Sempre usar pre-request scripts para autenticação
- Salvar variáveis automaticamente após operações
- Validar respostas com test scripts
- Auto-refresh de tokens quando expirados
2. Exemplos Reais
- Fazer requisições reais para capturar respostas
- Usar dados da especificação técnica quando disponível
- Criar dados sintéticos realistas quando necessário
3. Cobertura Completa
- Documentar TODOS os casos de erro possíveis
- Incluir exemplos para cada validação
- Documentar regras de negócio importantes
4. Manutenibilidade
- Usar variáveis para valores repetidos
- Organizar hierarquicamente
- Nomenclatura clara e consistente
- Gerar collections a partir de OpenAPI quando possível
5. Reduzir Repetição
- Padronizar endpoints e variáveis
- Criar snippets de pre-request/test reutilizáveis
- Gerar collections a partir de OpenAPI
- Manter templates de request com placeholders
6. Tratamento de Erros
Quando encontrar erros durante a documentação:
-
Documentar o erro:
- Request completo (método, URL, headers, body)
- Response completo (status, headers, body)
- Descrição clara do problema
-
Informar necessidade de correção:
- Identificar se é bug na API ou na documentação
- Sugerir correção quando possível
-
Reteste completo:
- Após correção, refazer TODOS os testes do início ao fim
- Validar que nada quebrou
- Confirmar que o erro foi corrigido
Estrutura de Diretórios
projeto/
├── postman/
│ ├── collection.json # Collection principal
│ ├── environment.json # Environment padrão
│ └── environment.prod.json # Environment de produção (gitignored)
├── scripts/
│ └── run_newman.sh # Script de execução Newman
├── .github/
│ └── workflows/
│ └── postman-ci.yml # CI/CD workflow
└── reports/
└── postman/ # Relatórios Newman (gitignored)
├── junit-results.xml
└── report.html
Referências
Templates e Snippets
Pre-request Snippet (JWT Auto-refresh)
if (pm.environment.get('auth_token')) {
pm.request.headers.upsert({
key: 'Authorization',
value: 'Bearer ' + pm.environment.get('auth_token')
});
}
let expiresAt = pm.environment.get('auth_expires_at');
if (expiresAt && Date.now() >= parseInt(expiresAt)) {
pm.sendRequest({
url: pm.environment.get('base_url') + '/auth/refresh',
method: 'POST',
header: { 'Content-Type': 'application/json' },
body: {
mode: 'raw',
raw: JSON.stringify({
refresh_token: pm.environment.get('refresh_token')
})
}
}, function (err, res) {
if (!err && res.code === 200) {
let j = res.json();
if (j.access_token) pm.environment.set('auth_token', j.access_token);
if (j.refresh_token) pm.environment.set('refresh_token', j.refresh_token);
if (j.expires_in) pm.environment.set('auth_expires_at', Date.now() + (j.expires_in * 1000));
}
});
}
Test Snippet (Salvar Tokens)
pm.test("Login works", function () {
pm.response.to.have.status(200);
});
let r = pm.response.json();
if (r.access_token) pm.environment.set('auth_token', r.access_token);
if (r.refresh_token) pm.environment.set('refresh_token', r.refresh_token);
if (r.expires_in) pm.environment.set('auth_expires_at', Date.now() + (r.expires_in * 1000));
Automação Avançada
Geração de Collection a partir de OpenAPI
Fluxo recomendado:
- Gerar/atualizar OpenAPI (Express:
swagger-jsdoc, Django: drf-spectacular, FastAPI: automático)
- Converter OpenAPI → Postman (
openapi-to-postmanv2)
- Injetar snippets de pre-request/tests que gerenciam JWT automaticamente
- Gerar ambiente com variáveis e placeholders
- Commitar collection/ambientes em
postman/ no repo
- CI executa Newman automaticamente
- Opcional: Publicar no Postman API via
collections.publish
Observações Finais
- Mantenha collections pequenas e bem nomeadas — prefira gerar via OpenAPI quando possível
- Testes determinísticos — evite dados dependentes de estado sem reset jobs
- Concentre repetição em:
- Collection-level scripts (pre-request/tests)
- Environment variables
- Templates gerados pela skill
Templates e Exemplos: Ver core/templates/postman-collection/ para templates completos de collection, environment e scripts de automação