Scripts en Postman: pre-request para preparar datos y contexto

Capítulo 6

Tiempo estimado de lectura: 11 minutos

+ Ejercicio

¿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_).

Continúa en nuestra aplicación.
  • Escuche el audio con la pantalla apagada.
  • Obtenga un certificado al finalizar.
  • ¡Más de 5000 cursos para que explores!
O continúa leyendo más abajo...
Download App

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 CryptoJS o crypto.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' });
}

Ahora responde el ejercicio sobre el contenido:

¿Cuál es el propósito principal de un script pre-request en Postman, en contraste con los tests?

¡Tienes razón! Felicitaciones, ahora pasa a la página siguiente.

¡Tú error! Inténtalo de nuevo.

El pre-request se ejecuta antes de enviar la solicitud y se enfoca en preparar la ejecución (datos, headers/body dinámicos, tokens y dependencias). Los tests se usan para validar la respuesta después.

Siguiente capítulo

Tests en Postman: aserciones y validación de contratos de API

Arrow Right Icon
Portada de libro electrónico gratuitaPostman en la Práctica: Pruebas de API desde Cero hasta la Automatización
50%

Postman en la Práctica: Pruebas de API desde Cero hasta la Automatización

Nuevo curso

12 páginas

Descarga la aplicación para obtener una certificación gratuita y escuchar cursos en segundo plano, incluso con la pantalla apagada.