endpoint.pe

Autocompletar nombres con el DNI en formularios web

Patrón de UX para autocompletar nombres y apellidos a partir del DNI: debounce, estados de carga y error, corrección manual, caché y ejemplo en JavaScript.

eEquipo endpoint.pe•21 may. 20268 min de lectura

Un formulario que pide DNI, nombres y apellidos por separado tiene tres campos que el usuario puede escribir mal. Uno que pide solo el DNI y rellena el resto tiene uno. Esa diferencia se nota en la tasa de abandono, en los errores de tipeo y en la calidad de los datos que llegan a tu base.

El patrón es conocido: la persona escribe sus 8 dígitos, el sistema consulta y los nombres aparecen solos. Hacerlo bien tiene más detalles de los que parece: cuándo disparar la consulta, qué mostrar mientras carga, qué pasar si no hay resultado, cómo permitir corregir y cómo no gastar una consulta por cada tecla.

Aquí vas a ver el patrón completo, con la arquitectura correcta (el token de la API nunca toca el navegador) y código en JavaScript tanto del backend como del frontend.


El patrón: escribe el DNI y aparecen los nombres

El flujo ideal desde el punto de vista del usuario tiene cuatro momentos:

  1. Escribe el DNI. Mientras no tenga 8 dígitos, no pasa nada.
  2. Al completar los 8 dígitos, el formulario muestra un indicador de carga pequeño junto al campo.
  3. Si hay resultado, los campos de nombres y apellidos se rellenan y quedan editables.
  4. Si no hay resultado o falla la consulta, los campos quedan vacíos y editables, con un mensaje breve y sin bloquear el envío.

El punto 4 es el que más sistemas descuidan. Una consulta sin resultado no significa que la persona no exista: puede haber un error de tipeo, un documento recién emitido o una caída temporal. El formulario tiene que seguir funcionando a mano.

Lo que devuelve una consulta de DNI son nombres y apellidos. No esperes dirección, fecha de nacimiento ni foto: esos datos no forman parte de lo que una consulta pública entrega.

Arquitectura: el navegador nunca ve el token

La tentación es llamar a la API directamente desde el frontend con fetch. No lo hagas. Cualquier persona que abra las herramientas de desarrollo del navegador verá tu token y podrá consumir tus créditos desde donde quiera.

La arquitectura correcta tiene tres piezas:

  • Frontend: captura el DNI, llama a tu propio backend y pinta el resultado.
  • Backend: valida el formato, aplica límites por usuario o por IP, consulta la API de DNI de endpoint.pe con el token guardado en una variable de entorno y devuelve solo lo que el formulario necesita.
  • Caché (opcional pero recomendable): guarda respuestas recientes para no repetir consultas.

Además de proteger el token, el backend te permite auditar cada consulta, aplicar reglas de negocio y cambiar de proveedor sin tocar el frontend. En seguridad de tokens API hay más razones y cómo rotar tokens si uno se filtra.

El backend que expone la consulta

Un endpoint mínimo en Node con Express. Valida que sean exactamente 8 dígitos, consulta y traduce los códigos de la API a respuestas que el frontend entiende.

import express from "express";

const app = express();
app.use(express.json());

const TOKEN = process.env.ENDPOINT_TOKEN;

app.get("/api/dni/:dni", async (req, res) => {
  const dni = req.params.dni;

  if (!/^\d{8}$/.test(dni)) {
    return res.status(400).json({ error: "formato_invalido" });
  }

  try {
    const r = await fetch(`https://api.endpoint.pe/v1/dni/${dni}`, {
      headers: { Authorization: `Bearer ${TOKEN}` },
    });

    if (r.status === 404) return res.status(404).json({ error: "not_found" });
    if (r.status === 429) return res.status(503).json({ error: "reintentar" });
    if (!r.ok) return res.status(502).json({ error: "consulta_fallida" });

    const { data } = await r.json();
    return res.json({
      nombres: data.nombres,
      apellido_paterno: data.apellido_paterno,
      apellido_materno: data.apellido_materno,
    });
  } catch (e) {
    return res.status(502).json({ error: "consulta_fallida" });
  }
});

app.listen(3000);

Con el DNI de ejemplo 12345678 (un número ficticio para ilustrar), la respuesta sería:

{
  "success": true,
  "message": "Consulta exitosa",
  "data": {
    "nombres": "JUAN CARLOS",
    "apellido_paterno": "PÉREZ",
    "apellido_materno": "QUISPE"
  },
  "meta": {
    "fuente": "SUNAT",
    "consultado_en": "2026-10-12T07:40:12Z",
    "creditos_cobrados": 1,
    "creditos_disponibles": 14621
  }
}

Dos decisiones que vale la pena explicar. Primero, el 429 de la API (límite de velocidad) se traduce a un 503 para que el frontend muestre "intenta de nuevo" en lugar de un error definitivo. Segundo, el backend no devuelve la respuesta cruda: solo los tres campos que el formulario usa. Es una buena costumbre de proporcionalidad; en la Ley de protección de datos para desarrolladores explicamos por qué.

Añade un límite de peticiones por IP o por sesión (por ejemplo, con un middleware de rate limiting). Sin él, tu endpoint público se convierte en un proxy gratuito hacia tu cuenta.

El frontend: debounce, estados y corrección

El frontend tiene que resolver cuatro cosas: no disparar la consulta en cada tecla, cancelar consultas obsoletas, mostrar estados claros y dejar que el usuario corrija.

const dniInput = document.querySelector("#dni");
const nombres = document.querySelector("#nombres");
const apPaterno = document.querySelector("#apellido_paterno");
const apMaterno = document.querySelector("#apellido_materno");
const estado = document.querySelector("#dni-estado");

let timer = null;
let controlador = null;
let ultimoConsultado = "";

function mostrar(texto, tipo) {
  estado.textContent = texto;
  estado.dataset.tipo = tipo; // "cargando" | "ok" | "aviso" | ""
}

async function consultar(dni) {
  if (controlador) controlador.abort();
  controlador = new AbortController();
  mostrar("Buscando...", "cargando");

  try {
    const r = await fetch(`/api/dni/${dni}`, { signal: controlador.signal });

    if (r.status === 404) {
      mostrar("No encontramos ese DNI. Completa tus datos manualmente.", "aviso");
      return;
    }
    if (!r.ok) {
      mostrar("No pudimos verificar el DNI. Puedes continuar a mano.", "aviso");
      return;
    }

    const { data: d } = await r.json();
    nombres.value = d.nombres;
    apPaterno.value = d.apellido_paterno;
    apMaterno.value = d.apellido_materno;
    ultimoConsultado = dni;
    mostrar("Datos completados. Puedes corregirlos si hace falta.", "ok");
  } catch (e) {
    if (e.name !== "AbortError") {
      mostrar("Sin conexión. Completa tus datos manualmente.", "aviso");
    }
  }
}

dniInput.addEventListener("input", () => {
  const dni = dniInput.value.replace(/\D/g, "").slice(0, 8);
  dniInput.value = dni;
  clearTimeout(timer);

  if (dni.length !== 8) {
    mostrar("", "");
    return;
  }
  if (dni === ultimoConsultado) return;

  timer = setTimeout(() => consultar(dni), 400);
});

Qué hace cada parte:

  • Limpieza del valor: elimina todo lo que no sea dígito y corta a 8. Así el usuario puede pegar "DNI: 12345678" y funciona igual.
  • Debounce de 400 ms: si la persona sigue escribiendo o corrigiendo, no se dispara nada hasta que se detiene. Evita consultas intermedias y ahorra créditos.
  • AbortController: si el usuario cambia el DNI mientras una consulta está en vuelo, la anterior se cancela y no pisa el resultado nuevo.
  • Memoria del último consultado: si borra y vuelve a escribir el mismo número, no repite la consulta.
  • Campos siempre editables: el autocompletado rellena, no bloquea. Si RENIEC tiene un apellido con tilde y la persona lo escribe sin ella, puede ajustarlo.

El mensaje de estado va en un elemento con aria-live="polite" para que los lectores de pantalla lo anuncien sin interrumpir. Y el campo de DNI debería usar inputmode="numeric" para que en el celular aparezca el teclado numérico.

Caché y ahorro de créditos

Cada consulta a la API cuesta un crédito, y las consultas sin resultado no se cobran. Aun así, un formulario popular puede repetir el mismo DNI muchas veces: la misma persona comprando dos veces, un usuario que recarga la página, un vendedor que corrige un pedido.

Los nombres y apellidos de una persona cambian muy poco. Eso los vuelve candidatos ideales para una caché de larga duración en tu backend, en una tabla o en Redis, con el DNI como clave. Antes de llamar a la API, revisa la caché; si hay resultado reciente, respóndelo directo.

Dos cuidados. Primero, esa caché contiene datos personales, así que necesita las mismas protecciones que tu base de datos principal: cifrado, acceso restringido, tiempo de vida definido. Segundo, no caches los 404 por mucho tiempo: un DNI recién emitido puede no aparecer hoy y sí mañana. En caché de consultas RUC y DNI hay tiempos de vida sugeridos por tipo de dato.

Para dimensionar el consumo: endpoint.pe incluye 150 consultas gratis al mes sin tarjeta, suficientes para desarrollar y probar el patrón completo antes de decidir un plan en precios.

Detalles de UX que marcan la diferencia

Más allá del código, hay decisiones pequeñas que cambian la experiencia.

  • Orden de los campos. El DNI va primero. Si pides nombres antes, la persona ya los escribió cuando el autocompletado llega, y sentirá que el sistema le pisa su trabajo.
  • Indicador discreto. Un spinner pequeño dentro del campo o un texto gris debajo. Nada de modales ni overlays que tapen el formulario por una consulta de milisegundos.
  • Formato visual. RENIEC devuelve los nombres en mayúsculas. Decide si los muestras así o los conviertes a formato "Juan Carlos"; lo importante es ser consistente en toda la aplicación y guardar siempre la misma forma.
  • Ceros a la izquierda. Un DNI puede empezar con cero. Si lo guardas como número, lo pierdes. Guárdalo como texto, siempre. En cómo validar un DNI en tu sistema está la lista completa de errores de formato.
  • Verificación posterior. Si el usuario modificó los nombres que vinieron de la consulta, marca el registro como "editado manualmente". Es útil para auditoría y para decidir si vale la pena revalidar después.
  • Consulta en vivo para probar. Antes de escribir una línea, prueba el comportamiento real de la API con la consulta en vivo para ver qué devuelve con distintos números.

En resumen

  • El patrón reduce campos, errores de tipeo y abandono: la persona escribe 8 dígitos y el resto se completa.
  • El token de la API vive en el backend; el navegador solo habla con tu propio endpoint, que valida formato y aplica límites.
  • Dispara la consulta con debounce al completar 8 dígitos, cancela las consultas obsoletas y recuerda el último DNI consultado.
  • Muestra estados claros (cargando, completado, no encontrado, sin conexión) y deja los campos siempre editables.
  • Nunca bloquees el envío del formulario por una consulta fallida.
  • Cachea los resultados con tiempo de vida largo y protégelos como datos personales.

Preguntas frecuentes

¿Por qué no llamar a la API directamente desde el navegador?

Porque el token quedaría expuesto a cualquiera que abra las herramientas del navegador, y podría consumir tus créditos desde fuera de tu aplicación. Además, pasar por tu backend te permite validar, limitar, auditar y cachear.

¿Qué hago si la consulta devuelve 404?

Muestra un aviso breve y deja que la persona escriba sus datos. Un 404 puede ser un error de tipeo o un documento muy reciente. Esas consultas no se cobran en endpoint.pe, así que no te preocupes por el costo de los intentos fallidos.

¿Debo permitir que el usuario edite los nombres autocompletados?

Sí. El autocompletado es una ayuda, no una restricción. Deja los campos editables y, si cambian, marca el registro para revisarlo después. Bloquear los campos genera frustración cuando hay tildes, segundos nombres o datos desactualizados.

¿Cuánto tiempo puedo guardar en caché un resultado?

Los nombres cambian muy rara vez, así que una caché de semanas o meses es razonable para datos que ya están en tu sistema. Los 404, en cambio, conviene cachearlos poco o nada. Lo que guardes es un banco de datos personales y debe protegerse como tal.

Deja de copiar datos a mano. Automatízalo hoy mismo

Crea tu cuenta, copia tu token y empieza hoy con 150 consultas gratis al mes.