PRIMERO: Usa el skill padre neon para una visión general de Neon, cómo empezar, mejores prácticas de desarrollo y más.
Si el skill neon no está instalado, obténgelo de https://neon.com/docs/ai/skills/neon/SKILL.md o instálalo con:
npx skills add neondatabase/agent-skills --skill neon
Funciones de Neon
Esta es una característica en beta pública, actualmente disponible en us-east-2 y eu-central-1.
Las Funciones de Neon son manejadores HTTP de Node.js de larga duración desplegados en una rama de Neon. Cada función obtiene una URL HTTPS pública, se ejecuta en la misma región que tu base de datos y — si la rama tiene Postgres — recibe DATABASE_URL inyectada automáticamente. Las despliegas y gestionas a través del mismo CLI de Neon, neon.ts y la API que ya usas.
Usa este skill para ayudar al usuario a definir, ejecutar localmente, desplegar y gestionar funciones junto a su base de datos. Entrega una función desplegada con su URL de invocación, un bucle local neon dev funcionando, o una respuesta precisa de la documentación oficial de Neon.
Cuándo Usar
Recurre a las Funciones de Neon cuando la carga de trabajo sea un manejador de solicitud/respuesta que se beneficie de mantenerse activo y cerca de los datos:
- Flujos de solicitud/respuesta de larga duración que superan los límites de las funciones lambda. Los agentes que hacen varias llamadas a LLM e invocaciones de herramientas por solicitud, o la generación de imágenes/videos, superan rutinariamente los límites de ejecución de ~10–60s y las ventanas de streaming cortas de las funciones serverless tradicionales. Las Funciones de Neon son de larga duración: el manejador solo necesita comenzar a responder dentro de 15 minutos, y un stream abierto permanece activo mientras fluyan bytes. Eso es suficiente espacio para cargas de trabajo reales de agentes.
- Streaming con estado sin necesidad de Redis. Como una función permanece activa durante una solicitud, puede alojar un endpoint SSE o un servidor WebSocket y mantener la conexión abierta en el proceso — sin necesidad de un almacén de estado externo (Redis, etc.) solo para mantener coherente un stream. El estado del ámbito del módulo (un pool de
pg, un contador en memoria) persiste entre solicitudes en el mismo aislamiento. - Cómputo que debe estar junto a Postgres. La función se ejecuta en la misma región que la base de datos de la rama, por lo que no hay viajes de ida y vuelta entre regiones en cada consulta.
DATABASE_URLse inyecta por ti. - Un backend que se ramifica con tus datos. Cada rama ejecuta su propia versión de la función en su propia URL, contra su propia base de datos aislada (y almacenamiento, y puerta de enlace). Las previsualizaciones de despliegue, CI y entornos de desarrollo obtienen cada uno un backend autocontenido — desplegar en una rama hija nunca afecta a la rama padre.
- Webhooks, bots y trabajo posterior a la respuesta. Manejadores de webhook que se distribuyen en múltiples escrituras a la base de datos, bots de Discord/WebSocket, y seguimientos de "disparar y olvidar" mediante
waitUntil(analíticas, registros de auditoría) encajan todos. - Trabajo HTTP recurrente. Un Disparador de Función hace un POST a la función en un cron (
type: "schedule"). Mismo manejadorfetch, mismo límite de 15 minutos para el primer byte. Ver Disparadores de Función.
Si la carga de trabajo es un sitio estático puro, o algo que debe ejecutarse fuera de las regiones compatibles (us-east-2, eu-central-1) hoy, esta no es la herramienta adecuada todavía (ver Límites de Tiempo de Espera y de Ejecución y Disponibilidad).
Qué Hace
- De larga duración y serverless — Construido para servidores WebSocket (ver Servidores WebSocket), endpoints SSE (ver Eventos Enviados por el Servidor (SSE)), streams HTTP largos de agentes y APIs. Se escala a cero cuando está inactivo.
- Manejador estándar web — Una función es cualquier exportación por defecto con un método
fetch(request)que devuelve unResponse(compatible con Workers/WinterTC). Una aplicación Hono exporta exactamente esa forma, así queexport default appfunciona directamente. Se ejecuta en Node.js 24, por lo que todas las API de Node están disponibles. - Cerca de tu base de datos — Se ejecuta en la región de la rama;
DATABASE_URLse inyecta automáticamente cuando la rama tiene Postgres. - Ramaificable — Cada rama ejecuta su propia versión de la función en su propia URL contra su propio estado aislado.
- Mismo CLI/API — Despliega y gestiona a través de
neon,neon.tso la API de Neon. - Disparadores de Función — Neon hace un POST a la función en un cron. Ver Disparadores de Función.
Disponibilidad
Verifica esta condición antes de configurar nada: Las Funciones de Neon es una característica en beta pública actualmente disponible en us-east-2 y eu-central-1. Confirma que el proyecto de Neon del usuario esté en una de estas regiones. El uso de las Funciones no se factura durante la beta pública.
Arquitectura: Dónde encajan las Funciones
Neon (incluidas las Funciones) es primitivas de backend, no alojamiento de aplicaciones completas. Aloja tu aplicación en Vercel (o Netlify, u otro host de frontend/aplicación); las Funciones son la porción de larga duración y con estado de tu backend que vive junto a tus datos. Se componen con esa plataforma de dos maneras:
- Agregar una Función a una aplicación full-stack. Tu aplicación Next.js / TanStack Start en Vercel (o Netlify) posee la UI, autenticación (ej. Neon Auth) y se comunica directamente con Lakebase Postgres y Object Storage. Cuando una carga de trabajo supera los límites serverless cortos del host — un servidor WebSocket o SSE, o un agente de larga duración que expiraría — mueve solo esa pieza a una Función de Neon. (Ver Funciones como un Backend de Agente para el patrón cliente-directo.)
- Ejecutar todo el plano de control del backend en Funciones. Especialmente cuando el frontend es solo cliente — TanStack Router, React Router en modo cliente y SPA similares alojadas en Vercel o Netlify — el cliente llama a las Funciones directamente. Construye APIs REST y agentes de solicitud/respuesta, aloja servidores MCP, y ejecuta cualquier cosa con estado o que pertenezca cerca de Postgres y Object Storage.
De cualquier manera, asegura una Función como cualquier API REST independiente: verifica un JWT o clave de API al principio del manejador (ver la ADVERTENCIA bajo Funciones como un Backend de Agente). Como una Función es solo tu backend, puedes mover piezas entre tu host y Neon — reubicar un agente o un servidor WebSocket con estado en una Función cuando necesite más tiempo de ejecución, y de vuelta si es necesario.
Configuración
Las funciones se declaran en neon.ts (ver el skill neon para el flujo de trabajo centrado en ramas y los conceptos básicos de neon.ts). Agrega @neon/config y declara funciones bajo preview.functions, claveadas por slug:
// neon.ts
import { defineConfig } from "@neon/config/v1";
export default defineConfig({
preview: {
functions: {
todos: {
// slug: ^[a-z0-9]{1,20}$ — letras minúsculas/dígitos, sin guiones
name: "todo api", // solo etiqueta de visualización
source: "src/index.ts", // archivo de entrada, relativo a neon.ts
},
},
},
});
El slug es la identidad permanente de la función (aparece en la URL de invocación y comandos del CLI) y no se puede cambiar después del primer despliegue. Usa name para una etiqueta legible por humanos.
Una función mínima — una aplicación Hono que consulta el Postgres de la rama a través de DATABASE_URL inyectada:
// src/index.ts
import { Hono } from "hono";
import { drizzle } from "drizzle-orm/node-postgres";
import { Pool } from "pg";
import { parseEnv } from "@neon/env";
import { attachDatabasePool } from "@neon/functions";
import config from "../neon";
import { todos } from "./db/schema";
const env = parseEnv(config);
const pool = new Pool({ connectionString: env.postgres.databaseUrl, max: 5 });
attachDatabasePool(pool);
const db = drizzle(pool);
const app = new Hono();
app.get("/", (c) => c.text("Neon + Hono + Drizzle"));
app.post("/todos", async (c) => {
const { text } = await c.req.json<{ text: string }>();
const [row] = await db.insert(todos).values({ text }).returning();
return c.json(row, 201);
});
app.get("/todos", async (c) => c.json(await db.select().from(todos)));
export default app;
Crea el pool de pg en el ámbito del módulo (reutilizado entre solicitudes en el mismo aislamiento) y mantén max pequeño (ej. 5), ya que cada aislamiento mantiene su propio pool. Llama a attachDatabasePool(pool) para que una desconexión inactiva no sea un uncaughtException — ver Conectándose a Postgres.
parseEnv(config) requiere cada variable que el config implica. Una función que solo se comunica con Postgres a través de la URL agrupada puede limitarlo a solo esa clave — parseEnv entonces valida y devuelve solo lo que pediste (las claves autocompletan desde tu neon.ts):
const { postgres } = parseEnv(config, ["DATABASE_URL"]); // no la URL no agrupada, autenticación, etc.
const pool = new Pool({ connectionString: postgres.databaseUrl, max: 5 });
attachDatabasePool(pool);
Desarrollar Localmente y Desplegar
neon dev # sirve cada función en neon.ts con recarga en caliente; inyecta DATABASE_URL y amigos
neon deploy --env <archivo> # despliegue completo preferido desde neon.ts; --env es el archivo del que se leen las variables de entorno de la Función
Mantén .env o .env.local actualizado con cada clave bajo preview.functions.*.env. neon env pull escribe solo las variables gestionadas por Neon; agrega los secretos de la Función a ese archivo, luego pásalo como --env. neon deploy --env <archivo> carga ese archivo en process.env cada vez, luego sube esos valores. Un valor faltante es undefined y defineConfig lanza un error. Omite la clave de neon.ts si no quieres escribirla. Nunca conviertas un valor faltante de process.env en una cadena vacía (eso sube "" y elimina la clave activa). Una asignación vacía (KEY=) también es "". Usa process.env.X! cuando TypeScript necesite una aserción.
Para desplegar una sola función sin aplicar neon.ts: neon functions deploy <slug> --src src/index.ts (--src acepta el archivo de entrada o un directorio que contenga index.ts, index.mjs o index.js). El --env de ese comando es KEY=VALUE (repetible), no una ruta de archivo. Úsalo para una actualización dirigida de variables de entorno. Recupera la URL pública con neon functions get <slug> (el campo invocation_url, de la forma https://<id_rama>-<slug>.compute.<cell>.us-east-2.aws.neon.tech). Gestiona con neon functions list|get|delete.
Cuando neon checkout crea una nueva rama y un neon.ts está presente, aplica la política automáticamente. Esa creación-aplicación no carga --env. Si las variables de entorno de la Función leen process.env, ejecuta neon deploy --env <archivo> después del checkout (agrega --update-existing si el checkout ya creó la rama). Hacer checkout de una rama existente no redepliega; ejecuta neon deploy --env <archivo> explícitamente.
Infraestructura como Código de Neon (neon.ts)
El bloque preview.functions de Configuración es parte de neon.ts, el archivo de infraestructura como código de Neon — un archivo TypeScript declara cada función (su source, name de visualización y env) junto con cualquier otro servicio de la rama, en control de versiones (ver el skill neon para la referencia completa). Trátalo como Terraform para tu rama:
neon config status # imprime la configuración activa de la rama (funciones desplegadas)
neon config plan # previsualización en seco de lo que apply cambiaría
neon config apply --env <archivo> # empaqueta + despliega las funciones declaradas (neon deploy es un alias; pasa --env cuando las variables de entorno de la Función leen process.env)
Las funciones tienen ámbito de rama: cada rama ejecuta su propio despliegue en su propia URL. Cuando un neon.ts está presente, neon checkout aplica la política mientras crea una rama. Esa creación-aplicación no carga --env. Si las variables de entorno de la Función leen process.env, ejecuta neon deploy --env <archivo> después del checkout. Hacer checkout de una rama existente no redepliega — ejecuta neon deploy --env <archivo> para aplicar cambios.
El ajuste de despliegue por rama (ej. runtime) vive en el cierre branch, claveado por slug, para que pueda variar por rama sin cambiar qué funciones existen:
export default defineConfig({
preview: {
functions: { todos: { name: "todo api", source: "src/index.ts" } },
},
branch: (branch) => ({
preview: { functions: { todos: { runtime: "nodejs24" } } },
}),
});
Variables de Entorno
Neon inyecta cadenas de conexión y URLs de servicio con ámbito de rama en tiempo de ejecución — no las declaras ni las pasas en el momento del despliegue:
| Variable | Notas |
|---|---|
NEON_BRANCH | El nombre de la rama (ej. main, preview/foo). Inyectado en cada rama, incluida la predeterminada. |
DATABASE_URL | Cadena de conexión agrupada. Úsala para la mayoría de las consultas. Presente solo si la rama tiene Postgres. |
DATABASE_URL_UNPOOLED | Conexión directa. Úsala para migraciones, LISTEN/NOTIFY, transacciones de múltiples viajes. |
NEON_AUTH_BASE_URL | Presente cuando Neon Auth está habilitado en la rama. |
NEON_DATA_API_URL | Presente cuando la API de Datos está habilitada en la rama. |
El almacenamiento de objetos (AWS_*) y las variables de AI Gateway (NEON_AI_GATEWAY_*) también se inyectan cuando esos servicios están declarados — ver los skills neon-object-storage y neon-ai-gateway.
neon env pull / neon-env run / neon dev emiten NEON_BRANCH (y las cadenas de conexión) en tu entorno de desarrollo local también, por lo que las ejecuciones locales reflejan el tiempo de ejecución desplegado.
Tus propios secretos son por despliegue. Ruta preferida: decláralos en neon.ts y ejecuta neon deploy --env <archivo>. <archivo> es el archivo ignorado por git que env pull ya escribe (.env si ese archivo existe, de lo contrario .env.local). Env pull escribe solo variables gestionadas por Neon; agrega los secretos de las Funciones a ese archivo. Todas las claves de variables de entorno de la Función declaradas deben estar presentes. Omite una clave de neon.ts si no quieres escribirla. undefined significa que pediste escribir la clave y el valor falta (defineConfig lanza un error). Nunca conviertas un valor faltante de process.env en una cadena vacía: eso sube "" y elimina la clave activa. Una asignación vacía en el archivo (KEY=) también es "". Si TypeScript necesita una aserción, usa process.env.X!.
… [truncated for translation]