¿Qué es un script pre-request y cuándo usarlo?
Un pre-request script es código JavaScript que Postman ejecuta antes de enviar una request. Su objetivo es preparar el contexto: generar datos, construir headers o bodies dinámicos, resolver dependencias (por ejemplo, obtener/renovar tokens), y decidir si una request debe ejecutarse o saltarse. A diferencia de los tests (que validan la respuesta), el pre-request se enfoca en preparar la ejecución.
Usos típicos: (1) crear valores únicos para evitar colisiones (emails, IDs), (2) firmar requests (HMAC, timestamps), (3) inyectar headers calculados, (4) componer bodies a partir de variables, (5) refrescar tokens o reobtenerlos si expiran, (6) controlar flujos simples para evitar llamadas innecesarias.
Dónde escribirlos y cómo organizarlos
Niveles de ejecución
- Colección: patrones reutilizables para todas las requests (helpers, generación de datos estándar, manejo de auth común).
- Carpeta: lógica compartida por un grupo (por ejemplo, “Usuarios” o “Pagos”).
- Request: casos específicos (por ejemplo, un endpoint que requiere firma particular).
Postman ejecuta scripts de niveles superiores y luego los más específicos. Esto permite definir utilidades en colección y solo “configurar” en cada request.
Patrón recomendado: helpers + configuración
En la colección, define funciones reutilizables (helpers). En cada request, define variables “de entrada” (por ejemplo, needsAuth=true, signatureVersion=v2) y llama a los helpers.
Guía práctica paso a paso: preparar datos, headers y body
Paso 1: Generar valores aleatorios y determinísticos
En pre-request, genera datos y guárdalos en variables para reutilizarlos en URL, headers o body. Mantén consistencia de nombres (prefijos como gen_ o tmp_).
- Escuche el audio con la pantalla apagada.
- Obtenga un certificado al finalizar.
- ¡Más de 5000 cursos para que explores!
Descargar la aplicación
UUID
Si tu versión de Postman soporta crypto.randomUUID(), úsalo. Si no, usa un fallback.
// UUID (preferido si está disponible)
const uuid = (typeof crypto !== 'undefined' && crypto.randomUUID)
? crypto.randomUUID()
: pm.variables.replaceIn('{{$guid}}');
pm.variables.set('gen_uuid', uuid);Timestamps (epoch y ISO)
const now = new Date();
pm.variables.set('gen_ts_ms', String(now.getTime()));
pm.variables.set('gen_ts_s', String(Math.floor(now.getTime() / 1000)));
pm.variables.set('gen_ts_iso', now.toISOString());Email único (aleatorio controlado)
Una estrategia práctica es componer un email con timestamp y un sufijo aleatorio corto para evitar colisiones en ejecuciones concurrentes.
function randomInt(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
const suffix = randomInt(1000, 9999);
const email = `qa+${Date.now()}_${suffix}@example.com`;
pm.variables.set('gen_email', email);Valores determinísticos (reproducibles)
Para pruebas repetibles, a veces necesitas valores “pseudo-aleatorios” pero determinísticos a partir de una semilla (por ejemplo, un runId). Esto ayuda a depurar porque el dato se repite si la semilla es la misma.
// Hash simple determinístico (no criptográfico) para generar un número estable
function hashCode(str) {
let h = 0;
for (let i = 0; i < str.length; i++) {
h = ((h << 5) - h) + str.charCodeAt(i);
h |= 0;
}
return Math.abs(h);
}
const runId = pm.variables.get('run_id') || 'local';
const n = hashCode(runId) % 10000;
pm.variables.set('gen_det_suffix', String(n).padStart(4, '0'));
pm.variables.set('gen_det_email', `qa+${runId}_${pm.variables.get('gen_det_suffix')}@example.com`);Paso 2: Construir headers dinámicos
Cuando un header depende de variables (timestamp, token, firma), es más robusto construirlo en pre-request y añadirlo al request. Así evitas duplicar lógica en cada tab de headers.
// Ejemplo: headers dinámicos comunes
const ts = pm.variables.get('gen_ts_s') || String(Math.floor(Date.now()/1000));
pm.request.headers.upsert({ key: 'X-Request-Id', value: pm.variables.get('gen_uuid') || pm.variables.replaceIn('{{$guid}}') });
pm.request.headers.upsert({ key: 'X-Timestamp', value: ts });
// Authorization si existe token
const token = pm.variables.get('access_token');
if (token) {
pm.request.headers.upsert({ key: 'Authorization', value: `Bearer ${token}` });
}Snippet recomendado: usa upsert para evitar duplicados y asegurar que el valor se actualice en cada ejecución.
Paso 3: Componer bodies basados en variables
En lugar de escribir JSON “fijo”, compón el body desde variables generadas. Esto es útil cuando el payload depende de datos calculados o de dependencias previas.
// Construcción de body JSON desde variables
const body = {
idempotencyKey: pm.variables.get('gen_uuid'),
email: pm.variables.get('gen_email'),
createdAt: pm.variables.get('gen_ts_iso'),
metadata: {
runId: pm.variables.get('run_id') || 'local'
}
};
pm.request.body.update(JSON.stringify(body));
pm.request.headers.upsert({ key: 'Content-Type', value: 'application/json' });Nota: pm.request.body.update funciona cuando el body es editable (por ejemplo, modo raw). Si tu request usa otro modo, ajusta la estrategia (por ejemplo, variables en el body y {{...}}).
Firmar requests: patrón HMAC con timestamp
Un caso frecuente es firmar la request con un secreto compartido. El patrón típico: construir un string to sign con método, path, timestamp y body; luego calcular HMAC y enviarlo en headers.
Ejemplo: HMAC-SHA256
Este ejemplo usa la librería CryptoJS (disponible en el sandbox de Postman en muchos entornos). Ajusta el string to sign a lo que exija tu API.
// Config
const apiKey = pm.variables.get('api_key');
const apiSecret = pm.variables.get('api_secret');
// Datos base
const method = pm.request.method;
const url = pm.request.url;
const path = '/' + url.path.join('/');
const ts = String(Math.floor(Date.now() / 1000));
// Body (si existe)
let rawBody = '';
if (pm.request.body && pm.request.body.mode === 'raw' && pm.request.body.raw) {
rawBody = pm.request.body.raw;
}
// String to sign (ejemplo)
const toSign = [method, path, ts, rawBody].join('\n');
// Firma
const signature = CryptoJS.HmacSHA256(toSign, apiSecret).toString(CryptoJS.enc.Hex);
// Headers
pm.request.headers.upsert({ key: 'X-Api-Key', value: apiKey });
pm.request.headers.upsert({ key: 'X-Timestamp', value: ts });
pm.request.headers.upsert({ key: 'X-Signature', value: signature });Pauta: guarda toSign en una variable temporal cuando estés depurando para comparar con el backend.
pm.variables.set('tmp_string_to_sign', toSign);Manejo de tokens: reuso, refresh o reobtención
En pre-request puedes decidir si usar el token actual, refrescarlo o reobtenerlo. La clave es tener: (1) un lugar donde guardar access_token, (2) un criterio de expiración, (3) una forma de obtener uno nuevo.
Patrón 1: expiración por tiempo (token con TTL)
Guarda access_token y token_expires_at (epoch ms). Antes de cada request, verifica si está por expirar y actúa.
function isTokenValid(skewMs = 30000) {
const token = pm.variables.get('access_token');
const exp = Number(pm.variables.get('token_expires_at') || 0);
return !!token && (Date.now() + skewMs) < exp;
}
if (isTokenValid()) {
// Token OK, no hacemos nada
} else {
// Señalamos que hay que obtener/renovar token
pm.variables.set('needs_token', 'true');
}Patrón 2: obtener token en el momento con pm.sendRequest
Si needs_token es true, llama al endpoint de auth desde el pre-request, guarda el token y continúa. Esto evita depender de ejecutar manualmente una request de login antes.
function setToken(token, expiresInSec) {
pm.variables.set('access_token', token);
pm.variables.set('token_expires_at', String(Date.now() + (expiresInSec * 1000)));
}
function fetchToken(done) {
const authUrl = pm.variables.get('auth_url');
const clientId = pm.variables.get('client_id');
const clientSecret = pm.variables.get('client_secret');
pm.sendRequest({
url: authUrl,
method: 'POST',
header: { 'Content-Type': 'application/json' },
body: {
mode: 'raw',
raw: JSON.stringify({ clientId, clientSecret })
}
}, (err, res) => {
if (err) return done(err);
const json = res.json();
setToken(json.access_token, json.expires_in || 3600);
done(null);
});
}
const needsToken = pm.variables.get('needs_token') === 'true';
if (needsToken) {
fetchToken((err) => {
if (err) {
// Si falla, deja evidencia para depurar
pm.variables.set('tmp_token_error', String(err));
}
pm.variables.unset('needs_token');
// El request actual continuará; el header Authorization se puede upsert luego
});
}Snippet recomendado: después de asegurar token, añade Authorization con upsert (como se mostró antes). Si tu API requiere refresh token, el mismo patrón aplica cambiando el payload del pm.sendRequest.
Patrón 3: refresh token si existe, si no reobtener
Implementa una función que intente refresh y, si no hay refresh_token o falla, haga reobtención. Mantén el flujo simple y registrable.
function refreshOrReauth(done) {
const refreshToken = pm.variables.get('refresh_token');
if (!refreshToken) {
return fetchToken(done);
}
const refreshUrl = pm.variables.get('refresh_url');
pm.sendRequest({
url: refreshUrl,
method: 'POST',
header: { 'Content-Type': 'application/json' },
body: { mode: 'raw', raw: JSON.stringify({ refreshToken }) }
}, (err, res) => {
if (err || res.code >= 400) {
return fetchToken(done);
}
const json = res.json();
pm.variables.set('refresh_token', json.refresh_token || refreshToken);
pm.variables.set('access_token', json.access_token);
pm.variables.set('token_expires_at', String(Date.now() + ((json.expires_in || 3600) * 1000)));
done(null);
});
}Control de flujos simples desde pre-request
Además de preparar datos, puedes controlar decisiones: saltar requests si no aplica, o redirigir el flujo a otra request. Esto es útil para evitar fallos en cascada cuando falta una precondición.
Saltar una request si falta una dependencia
Ejemplo: si no existe un user_id requerido, salta a una request que lo cree o detén el flujo.
const userId = pm.variables.get('user_id');
if (!userId) {
// Opción A: saltar a una request llamada "Create User"
postman.setNextRequest('Create User');
}
Evitar loops: contador de reintentos
Cuando implementas reintentos (por ejemplo, reauth), usa un contador para no entrar en bucles.
const retries = Number(pm.variables.get('tmp_retries') || 0);
if (retries >= 2) {
// Detener ejecución de la colección
postman.setNextRequest(null);
} else {
pm.variables.set('tmp_retries', String(retries + 1));
}Snippets reutilizables recomendados (biblioteca personal)
1) Helper de lectura segura de JSON
function safeJsonParse(str, fallback = {}) {
try { return JSON.parse(str); } catch (e) { return fallback; }
}2) Normalizar base URL + path
function joinUrl(base, path) {
return base.replace(/\/$/, '') + '/' + path.replace(/^\//, '');
}3) Upsert de múltiples headers
function upsertHeaders(map) {
Object.keys(map).forEach(k => pm.request.headers.upsert({ key: k, value: String(map[k]) }));
}4) Generador de idempotency key
function idempotencyKey(prefix = 'idem') {
const guid = (typeof crypto !== 'undefined' && crypto.randomUUID)
? crypto.randomUUID()
: pm.variables.replaceIn('{{$guid}}');
return `${prefix}-${guid}`;
}
pm.variables.set('gen_idempotency', idempotencyKey());Pautas para scripts legibles y mantenibles
- Funciones pequeñas: encapsula (generación, firma, token) en funciones; evita bloques largos en línea.
- Nombres consistentes: prefijos por intención:
gen_(generado),tmp_(temporal),cfg_(config),auth_(auth). - Comentarios útiles: explica el “por qué” (criterio de expiración, formato de firma), no lo obvio.
- Evita magia: centraliza constantes (skew de expiración, versiones de firma) al inicio del script.
- Depuración controlada: guarda trazas en variables temporales (
tmp_string_to_sign,tmp_token_error) y límpialas cuando ya no se necesiten. - Reutilización por niveles: helpers en colección; configuración mínima en request.
- Compatibilidad: si dependes de
CryptoJSocrypto.randomUUID, incluye fallback o valida disponibilidad.
Ejemplo integrado: plantilla de pre-request “todo en uno” (adaptable)
Esta plantilla muestra una estructura típica: (1) generar contexto, (2) asegurar token, (3) preparar headers, (4) componer body. Ajusta nombres y endpoints.
// ===== Configuración =====
const SKEW_MS = 30000;
// ===== Helpers =====
function ensureUuid() {
const uuid = (typeof crypto !== 'undefined' && crypto.randomUUID)
? crypto.randomUUID()
: pm.variables.replaceIn('{{$guid}}');
pm.variables.set('gen_uuid', uuid);
}
function ensureTimestamps() {
const now = new Date();
pm.variables.set('gen_ts_s', String(Math.floor(now.getTime() / 1000)));
pm.variables.set('gen_ts_iso', now.toISOString());
}
function isTokenValid() {
const token = pm.variables.get('access_token');
const exp = Number(pm.variables.get('token_expires_at') || 0);
return !!token && (Date.now() + SKEW_MS) < exp;
}
function setAuthHeader() {
const token = pm.variables.get('access_token');
if (token) pm.request.headers.upsert({ key: 'Authorization', value: `Bearer ${token}` });
}
function fetchToken(done) {
pm.sendRequest({
url: pm.variables.get('auth_url'),
method: 'POST',
header: { 'Content-Type': 'application/json' },
body: {
mode: 'raw',
raw: JSON.stringify({
clientId: pm.variables.get('client_id'),
clientSecret: pm.variables.get('client_secret')
})
}
}, (err, res) => {
if (err) return done(err);
const json = res.json();
pm.variables.set('access_token', json.access_token);
pm.variables.set('token_expires_at', String(Date.now() + ((json.expires_in || 3600) * 1000)));
done(null);
});
}
// ===== Ejecución =====
ensureUuid();
ensureTimestamps();
// Headers base
pm.request.headers.upsert({ key: 'X-Request-Id', value: pm.variables.get('gen_uuid') });
pm.request.headers.upsert({ key: 'X-Timestamp', value: pm.variables.get('gen_ts_s') });
// Token
if (isTokenValid()) {
setAuthHeader();
} else {
fetchToken((err) => {
if (err) pm.variables.set('tmp_token_error', String(err));
setAuthHeader();
});
}
// Body dinámico (si aplica)
const email = pm.variables.get('gen_email') || `qa+${Date.now()}@example.com`;
pm.variables.set('gen_email', email);
const payload = {
requestId: pm.variables.get('gen_uuid'),
email,
ts: pm.variables.get('gen_ts_iso')
};
if (pm.request.body && pm.request.body.mode === 'raw') {
pm.request.body.update(JSON.stringify(payload));
pm.request.headers.upsert({ key: 'Content-Type', value: 'application/json' });
}