wandres.dev
HYPERDRIVE · acelerar tu Postgres

Configurar y conectar: de la cadena al primer SELECT

El camino completo de cero a consultar: crear la configuración con wrangler hyperdrive create y su cadena de conexión, declarar el binding en wrangler.jsonc, activar nodejs_compat porque los drivers de Postgres dependen de APIs de Node, e instanciar un cliente por petición con postgres.js o node-postgres sobre env.HYPERDRIVE.connectionString. Incluye el equivalente para MySQL con campos discretos, el desarrollo local con localConnectionString y por qué la caché no actúa en local.

⏱ 16 min

La teoría de Hyperdrive es más difícil que su práctica. Una vez entendidos el pool y la caché, ponerlo en marcha son cuatro pasos cortos: crear una configuración que guarde la cadena de conexión a tu base, declararla como binding, activar la compatibilidad con Node porque los drivers de Postgres la necesitan, y escribir el mismo SQL de siempre con el driver de siempre. Lo único que cambia respecto a tu código actual es de dónde sale la cadena de conexión, y ese detalle diminuto es también el que reordena por completo el modelo de seguridad.

🎯 Al terminar esta lección sabrás
  • Crear una configuración de Hyperdrive con wrangler hyperdrive create a partir de tu cadena de conexión.
  • Declarar el binding en wrangler.jsonc y entender por qué hace falta nodejs_compat.
  • Consultar con postgres.js o node-postgres desde env.HYPERDRIVE.connectionString.
  • Configurar el desarrollo local y saber qué se comporta distinto en tu máquina.

Crear la configuración

Una configuración de Hyperdrive es un objeto en tu cuenta que guarda cómo llegar a tu base y con qué credenciales. Se crea una sola vez, con los datos que ya tienes: la dirección o el nombre de máquina, el puerto, el usuario, la contraseña y el nombre de la base. Casi todos los proveedores te dan esa información ya montada en la cadena de conexión estándar que los drivers entienden.

# Postgres: postgres://USUARIO:CLAVE@MAQUINA:PUERTO/BASE
npx wrangler hyperdrive create mi-postgres \
  --connection-string="postgres://usuario:clave@db.ejemplo.com:5432/produccion"

# MySQL: mysql://USUARIO:CLAVE@MAQUINA:PUERTO/BASE
npx wrangler hyperdrive create mi-mysql \
  --connection-string="mysql://usuario:clave@db.ejemplo.com:3306/produccion"

Si todo va bien, el comando imprime la configuración recién creada con el único dato que necesitas conservar: el id.

{
  "hyperdrive": [
    {
      "binding": "HYPERDRIVE",
      "id": "57b7076f58be42419276f058a8968187"
    }
  ]
}

Fíjate en lo que no aparece ahí: tu usuario y tu contraseña. Se quedaron dentro de la configuración, en la plataforma, y no vuelven a salir. A partir de este momento tu proyecto conoce un identificador opaco, no unas credenciales, y esa es la primera consecuencia práctica del modelo de bindings aplicado a una base tradicional.

💡
Tu cortafuegos necesita saber esto

Hyperdrive se conecta a tu base desde los rangos de direcciones de Cloudflare, compartidos con el resto de la plataforma. Si tu base vive detrás de un cortafuegos que solo admite orígenes concretos —lo habitual en una instancia gestionada seria—, tendrás que permitir esos rangos antes de que la primera consulta funcione. Es el fallo de arranque más común y el más frustrante, porque el error que ves es un tiempo de espera agotado y no dice nada sobre cortafuegos.

El binding y nodejs_compat

Con el identificador en la mano, el binding se declara en wrangler.jsonc igual que cualquier otro recurso. El nombre que elijas es el que aparecerá en env, y debe ser un identificador válido de JavaScript.

{
  "name": "mi-api",
  "main": "src/index.ts",
  "compatibility_date": "2026-06-25",
  "compatibility_flags": ["nodejs_compat"],
  "observability": { "enabled": true },
  "hyperdrive": [
    {
      "binding": "HYPERDRIVE",
      "id": "57b7076f58be42419276f058a8968187"
    }
  ]
}

La línea que más gente olvida es nodejs_compat, y su ausencia produce un error desconcertante en tiempo de ejecución que habla de un módulo node: que no existe. La razón es sencilla en cuanto se piensa: a diferencia de D1 o KV, aquí no usas una API de la plataforma sino un driver de base de datos real, escrito para Node, que abre un socket, maneja búferes y emite eventos. Ese driver necesita que el runtime le ofrezca las APIs de Node que da por hechas, y esa es exactamente la bandera que las enciende. Sin ella no falla la configuración: falla el import de tu driver.

⚠️
Un binding no es solo comodidad, es una frontera

Guardar la cadena de conexión real en la configuración y no en tu proyecto elimina de golpe una familia entera de incidentes: credenciales de base de datos filtradas en el repositorio, en el historial de comandos, en las variables de un pipeline o en un volcado de registros. La cadena que tu Worker recibe en env.HYPERDRIVE.connectionString se genera para él y solo es utilizable desde él, así que aunque se imprimiera por error en un registro no serviría a nadie más para entrar en tu base. Estás sustituyendo una credencial transferible por una capacidad intransferible, que es la diferencia conceptual entre saber una contraseña y tener una llave que solo funciona en una cerradura.

Consultar con el driver de siempre

Instalado el driver, el código es indistinguible del que escribirías contra tu base directa. Con postgres.js —recomendado en versión 3.4.5 o posterior— basta con pasar la cadena que el binding expone.

import postgres from "postgres";

export interface Env {
  HYPERDRIVE: Hyperdrive;
}

export default {
  async fetch(request, env, ctx): Promise<Response> {
    // un cliente nuevo por peticion es barato: el pool lo mantiene Hyperdrive
    const sql = postgres(env.HYPERDRIVE.connectionString);
    try {
      const filas = await sql`SELECT id, nombre, precio FROM productos LIMIT 20`;
      return Response.json(filas);
    } catch (e) {
      console.error(e);
      return Response.json({ error: String(e) }, { status: 500 });
    }
  },
} satisfies ExportedHandler<Env>;

El comentario del código señala el cambio de hábito más importante al venir de un backend clásico. Allí, crear un cliente en cada petición era un pecado de rendimiento; aquí es lo correcto, porque el pool ya no vive en tu proceso sino en Hyperdrive, y tu cliente es una envoltura ligera sobre una conexión que se establece a milisegundos de distancia. No intentes conservar clientes entre invocaciones: el isolate que los guardaría puede desaparecer en cualquier momento.

Con node-postgres el patrón es el mismo cambiando la fachada, y con MySQL el binding expone además los campos por separado, porque mysql2 los prefiere así y necesita una opción extra para no recurrir a evaluación dinámica de código, que el runtime no permite.

import { createConnection } from "mysql2/promise";

const conexion = await createConnection({
  host: env.HYPERDRIVE.host,
  user: env.HYPERDRIVE.user,
  password: env.HYPERDRIVE.password,
  database: env.HYPERDRIVE.database,
  port: env.HYPERDRIVE.port,
  disableEval: true, // obligatorio en Workers: sin eval en el analizador
});

Tu ORM entra por la misma puerta. Drizzle, Prisma o Kysely se apoyan por debajo en estos mismos drivers, así que reciben la cadena o el cliente y siguen funcionando sin saber que hay un acelerador de por medio. El tipo Hyperdrive de la interfaz Env lo genera wrangler types a partir de tu configuración, de modo que el editor te avisa si escribes mal el nombre del binding.

flowchart LR
CFG[wrangler hyperdrive create] --> ID[id de configuracion]
ID --> BIND[binding en wrangler.jsonc]
BIND --> ENV[env.HYPERDRIVE en el Worker]
ENV --> DRV[driver postgres o mysql]
DRV --> DB[tu base existente]
style ENV fill:#89b4fa,color:#11111b
style DB fill:#a6e3a1,color:#11111b

Desarrollo local

En local no hay pool ni caché de Cloudflare que valga: tu Worker se conecta directamente a una base que tú le indiques. Ese destino se declara con localConnectionString junto al binding, o con una variable de entorno cuyo nombre termina con el nombre del binding.

{
  "hyperdrive": [
    {
      "binding": "HYPERDRIVE",
      "id": "57b7076f58be42419276f058a8968187",
      "localConnectionString": "postgres://usuario:clave@localhost:5432/desarrollo"
    }
  ]
}
# alternativa por variable de entorno, con el nombre del binding al final
export CLOUDFLARE_HYPERDRIVE_LOCAL_CONNECTION_STRING_HYPERDRIVE="postgres://usuario:clave@localhost:5432/desarrollo"
npx wrangler dev

Desde finales de 2025 ese destino puede ser también una base remota con TLS, indicando el modo correspondiente en la cadena —sslmode=require para Postgres, sslMode=REQUIRED para MySQL—, lo que permite desarrollar contra la base real sin renunciar a ejecutar el código en tu máquina.

Hay una diferencia de comportamiento que conviene tener presente para no sacar conclusiones falsas: con wrangler dev la caché de consultas no actúa. Todo lo que midas en local será el rendimiento sin caché. Si quieres comprobar el efecto real de la caché contra tu configuración desplegada, wrangler dev --remote ejecuta el Worker en la red de Cloudflare, con la contrapartida de que ya no corre en tu máquina.

La configuración correcta es la que convierte una decisión de seguridad en un hecho estructural

Merece la pena detenerse en lo que acaba de ocurrir, porque es fácil pasarlo por alto entre comandos y ficheros de configuración. Al mover la cadena de conexión desde tu código hasta una configuración de la plataforma, no has ganado comodidad: has cambiado de categoría un problema de seguridad. En el modelo tradicional, la credencial de la base es un secreto que viaja —vive en un fichero de entorno, se copia a un pipeline, se inyecta en un contenedor, se imprime sin querer en un volcado de error— y protegerla consiste en una disciplina permanente de no filtrarla, es decir, en confiar en que nadie del equipo se equivoque nunca en ninguna de las decenas de sitios por los que pasa. La disciplina como mecanismo de seguridad es notoriamente frágil, porque solo hace falta un descuido y porque el fallo es silencioso hasta que deja de serlo. En el modelo de bindings, la credencial no viaja: se queda en un solo sitio y lo que tu código recibe es una capacidad concreta, generada para ese Worker y utilizable únicamente desde él. La diferencia no es de grado sino de naturaleza, porque la propiedad deseada —que nadie ajeno pueda conectarse a tu base con lo que hay en tu repositorio— deja de depender del comportamiento correcto de las personas y pasa a depender de la estructura del sistema. Eso es lo que en ingeniería se llama hacer imposible el error en lugar de prohibirlo, y es la marca de las buenas primitivas: no te piden ser más cuidadoso, te quitan la ocasión de ser descuidado. Cuando evalúes cualquier plataforma, ese es el criterio que más te va a rendir con el tiempo, muy por encima de los números de rendimiento que ocupan los titulares: qué clase de errores hace estructuralmente imposibles, y cuáles sigue dejando en tus manos.

⚔️ Monta el camino completo
  1. Escribe la cadena de conexión de una base tuya en formato estándar y el comando que crearía su configuración de Hyperdrive.
  2. Declara el binding en wrangler.jsonc con nodejs_compat y explica qué error verías exactamente si olvidaras esa bandera.
  3. Escribe un manejador fetch que consulte con postgres.js y justifica por qué se crea un cliente nuevo en cada petición.
  4. Configura el desarrollo local por las dos vías —fichero y variable de entorno— y di qué comportamiento no podrás observar en tu máquina.
  5. Enumera los sitios por los que viajaría la contraseña de tu base en un despliegue tradicional y compáralo con lo que viaja ahora.