PRIMEIRO: Use o skill pai neon para uma visão geral do Neon, primeiros passos, melhores práticas de desenvolvimento com Neon e muito mais.
Se o skill neon não estiver instalado, baixe-o de https://neon.com/docs/ai/skills/neon/SKILL.md ou instale-o com:
npx skills add neondatabase/agent-skills --skill neon
Neon Functions
Este é um recurso em beta público, atualmente disponível em us-east-2 e eu-central-1.
Neon Functions são handlers HTTP Node.js de longa duração implantados em uma branch do Neon. Cada função recebe uma URL HTTPS pública, executa na mesma região que seu banco de dados e — se a branch tiver Postgres — recebe DATABASE_URL injetado automaticamente. Você implanta e gerencia através do mesmo CLI do Neon, neon.ts e API que já usa.
Use este skill para ajudar o usuário a definir, executar localmente, implantar e gerenciar funções próximas ao banco de dados. Entregue uma função implantada com sua URL de invocação, um loop de desenvolvimento local funcional com neon dev ou uma resposta precisa da documentação oficial do Neon.
Quando Usar
Recorra às Neon Functions quando a carga de trabalho for um handler de requisição/resposta que se beneficia de permanecer ativo e próximo aos dados:
- Fluxos de requisição/resposta de longa duração que excedem os limites de lambda. Agentes que fazem várias chamadas de LLM e invocações de ferramentas por requisição, ou geração de imagem/vídeo, rotineiramente ultrapassam os limites de execução de ~10–60s e as janelas curtas de streaming de funções serverless tradicionais. Neon Functions são de longa duração: o handler só precisa começar a responder dentro de 15 minutos, e um stream aberto permanece ativo enquanto bytes continuarem fluindo. Isso é espaço suficiente para cargas de trabalho reais de agentes.
- Streaming com estado sem precisar de Redis. Como uma função permanece ativa durante uma requisição, ela pode hospedar um endpoint SSE ou um servidor WebSocket e manter a conexão aberta in-process — sem necessidade de um armazenamento de estado externo (Redis, etc.) apenas para manter um stream coerente. O estado no escopo do módulo (um pool
pg, um contador em memória) persiste entre requisições no mesmo isolate. - Computação que deve ficar ao lado do Postgres. A função executa na mesma região que o banco de dados da branch, então não há viagens de ida e volta entre regiões em cada consulta.
DATABASE_URLé injetado para você. - Um backend que ramifica com seus dados. Cada branch executa sua própria versão da função em sua própria URL, contra seu próprio banco de dados isolado (e armazenamento e gateway). Implantações de preview, CI e ambientes de desenvolvimento cada um recebem um backend autocontido — implantar em uma branch filha nunca afeta a pai.
- Webhooks, bots e trabalho pós-resposta. Handlers de webhook que se ramificam em várias escritas no banco de dados, bots Discord/WebSocket e acompanhamentos fire-and-forget via
waitUntil(analytics, logs de auditoria) todos se encaixam. - Trabalho HTTP recorrente. Um Function Trigger faz POST para a função em um cron (
type: "schedule"). Mesmo handlerfetch, mesmo limite de 15 minutos para o primeiro byte. Veja Function Triggers.
Se a carga de trabalho for um site estático puro, ou algo que deve executar fora das regiões suportadas (us-east-2, eu-central-1) hoje, esta não é a ferramenta certa ainda (veja Timeouts e Limites de Runtime e Disponibilidade).
O Que Faz
- Longa duração e serverless — Construído para servidores WebSocket (veja Servidores WebSocket), endpoints SSE (veja Server-Sent Events (SSE)), streams HTTP longos de agentes e APIs. Ainda escala para zero quando ocioso.
- Handler padrão web — Uma função é qualquer exportação padrão com um método
fetch(request)retornando umResponse(compatível com Workers/WinterTC). Um app Hono exporta exatamente essa forma, entãoexport default appfunciona. Executa no Node.js 24, então todas as APIs Node estão disponíveis. - Próximo ao seu banco de dados — Executa na região da branch;
DATABASE_URLinjetado automaticamente quando a branch tem Postgres. - Ramificável — Cada branch executa sua própria versão da função em sua própria URL contra seu próprio estado isolado.
- Mesmo CLI/API — Implante e gerencie via
neon,neon.tsou a API do Neon. - Function Triggers — O Neon faz POST para a função em um cron. Veja Function Triggers.
Disponibilidade
Verifique esta pré-condição antes de configurar qualquer coisa: Neon Functions é um recurso em beta público atualmente disponível em us-east-2 e eu-central-1. Confirme que o projeto Neon do usuário está em uma dessas regiões. O uso de Functions não é cobrado durante o beta público.
Arquitetura: Onde as Functions se Encaixam
Neon (Functions incluídas) são primitivas de backend, não hospedagem de aplicação full-stack. Hospede seu aplicativo em Vercel (ou Netlify, ou outro host de frontend/aplicativo); Functions são a parte de longa duração e com estado do seu backend que vive próximo aos seus dados. Elas se compõem com essa plataforma de duas formas:
- Adicione uma Function a um aplicativo full-stack. Seu aplicativo Next.js / TanStack Start no Vercel (ou Netlify) possui UI, autenticação (ex.: Neon Auth) e se comunica diretamente com Lakebase Postgres e Object Storage. Quando uma carga de trabalho excede os limites serverless curtos do host — um servidor WebSocket ou SSE, ou um agente de longa duração que expiraria — mova apenas essa peça para uma Neon Function. (Veja Functions como um Backend de Agente para o padrão cliente-direto.)
- Execute todo o plano de controle do backend em Functions. Especialmente quando o frontend é apenas cliente — TanStack Router, React Router em modo cliente e SPAs similares hospedados no Vercel ou Netlify — o cliente chama Functions diretamente. Construa APIs REST e agentes de requisição/resposta, hospede servidores MCP e execute qualquer coisa com estado ou que pertença próximo ao Postgres e Object Storage.
De qualquer forma, proteja uma Function como qualquer API REST independente: verifique um JWT ou chave de API no topo do handler (veja o AVISO em Functions como um Backend de Agente). Como uma Function é apenas seu backend, você pode mover peças entre seu host e Neon — realocar um agente ou um servidor WebSocket com estado para uma Function quando precisar de mais tempo de execução, e de volta se necessário.
Configuração
Functions são declaradas em neon.ts (veja o skill neon para o fluxo de trabalho branch-first e conceitos básicos de neon.ts). Adicione @neon/config e declare functions sob preview.functions, chaveadas 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, sem hífens
name: "todo api", // rótulo de exibição apenas
source: "src/index.ts", // arquivo de entrada, relativo a neon.ts
},
},
},
});
O slug é a identidade permanente da função (aparece na URL de invocação e comandos CLI) e não pode ser alterado após o primeiro deploy. Use name para um rótulo legível por humanos.
Um exemplo mínimo — um app Hono que consulta o Postgres da branch via DATABASE_URL injetado:
// 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;
Crie o pool pg no escopo do módulo (reutilizado entre requisições no mesmo isolate) e mantenha max pequeno (ex.: 5), já que cada isolate mantém seu próprio pool. Chame attachDatabasePool(pool) para que uma desconexão ociosa não seja um uncaughtException — veja Conectando ao Postgres.
parseEnv(config) requer todas as variáveis que a config implica. Uma função que só se comunica com Postgres via URL poolizada pode escopar apenas para essa chave — parseEnv então valida e retorna apenas o que você pediu (as chaves são autocompletadas do seu neon.ts):
const { postgres } = parseEnv(config, ["DATABASE_URL"]); // não a URL não poolizada, auth, etc.
const pool = new Pool({ connectionString: postgres.databaseUrl, max: 5 });
attachDatabasePool(pool);
Desenvolver Localmente e Implantar
neon dev # serve cada função em neon.ts com hot reload; injeta DATABASE_URL & amigos
neon deploy --env <arquivo> # deploy completo preferido de neon.ts; --env é o arquivo de onde as variáveis de ambiente da função são lidas
Mantenha .env ou .env.local atualizado com todas as chaves sob preview.functions.*.env. neon env pull escreve apenas variáveis gerenciadas pelo Neon; adicione segredos da Function a esse arquivo e então passe-o como --env. neon deploy --env <arquivo> carrega esse arquivo em process.env cada vez e então faz upload desses valores. Um valor ausente é undefined e defineConfig lança um erro. Omita a chave de neon.ts se você não quiser escrevê-la. Nunca converta um valor process.env ausente para string vazia (isso faria upload de "" e deletaria a chave ativa). Uma atribuição vazia (KEY=) também é "". Use process.env.X! quando TypeScript precisar de uma asserção.
Para implantar uma única função sem aplicar neon.ts: neon functions deploy <slug> --src src/index.ts (--src aceita o arquivo de entrada ou um diretório contendo index.ts, index.mjs ou index.js). O --env desse comando é KEY=VALUE (repetível), não um caminho de arquivo. Use-o para uma atualização direcionada de variáveis de ambiente. Recupere a URL pública com neon functions get <slug> (o campo invocation_url, da forma https://<id_da_branch>-<slug>.compute.<cell>.us-east-2.aws.neon.tech). Gerencie com neon functions list|get|delete.
Quando neon checkout cria uma nova branch e um neon.ts está presente, ele aplica a política automaticamente. Esse create-apply não carrega --env. Se a Function env lê process.env, execute neon deploy --env <arquivo> após o checkout (adicione --update-existing se o checkout já criou a branch). Fazer checkout de uma branch existente não reimplanta; execute neon deploy --env <arquivo> explicitamente.
Neon Infrastructure as Code (neon.ts)
O bloco preview.functions de Configuração faz parte de neon.ts, o arquivo de infraestrutura como código do Neon — um arquivo TypeScript declara cada função (seu source, name de exibição e env) juntamente com quaisquer outros serviços da branch, no controle de versão (veja o skill neon para a referência completa). Trate-o como Terraform para sua branch:
neon config status # imprime a configuração ao vivo da branch (funções implantadas)
neon config plan # dry-run diff do que apply mudaria
neon config apply --env <arquivo> # empacota + implanta as funções declaradas (neon deploy é um alias; passe --env quando a Function env ler process.env)
Functions são escopo de branch: cada branch executa sua própria implantação em sua própria URL. Quando um neon.ts está presente, neon checkout aplica a política ao criar uma branch. Esse create-apply não carrega --env. Se a Function env lê process.env, execute neon deploy --env <arquivo> após o checkout. Fazer checkout de uma branch existente não reimplanta — execute neon deploy --env <arquivo> para aplicar alterações.
Ajustes de deploy por branch (ex.: runtime) ficam no closure branch, chaveado por slug, para que possa variar por branch sem alterar quais funções existem:
export default defineConfig({
preview: {
functions: { todos: { name: "todo api", source: "src/index.ts" } },
},
branch: (branch) => ({
preview: { functions: { todos: { runtime: "nodejs24" } } },
}),
});
Variáveis de Ambiente
O Neon injeta strings de conexão e URLs de serviço com escopo de branch em tempo de execução — você não as declara nem as passa no momento do deploy:
| Variável | Notas |
|---|---|
NEON_BRANCH | O nome da branch (ex.: main, preview/foo). Injetado em toda branch, incluindo a padrão. |
DATABASE_URL | String de conexão poolizada. Use para a maioria das consultas. Presente apenas se a branch tiver Postgres. |
DATABASE_URL_UNPOOLED | Conexão direta. Use para migrações, LISTEN/NOTIFY, transações com múltiplas idas e voltas. |
NEON_AUTH_BASE_URL | Presente quando Neon Auth está habilitado na branch. |
NEON_DATA_API_URL | Presente quando a API de Dados está habilitada na branch. |
Armazenamento de objetos (AWS_*) e AI Gateway (NEON_AI_GATEWAY_*) também são injetados quando esses serviços são declarados — veja os skills neon-object-storage e neon-ai-gateway.
neon env pull / neon-env run / neon dev emitem NEON_BRANCH (e as strings de conexão) em seu ambiente de desenvolvimento local também, então execuções locais espelham o runtime implantado.
Seus próprios segredos são por implantação. Caminho preferido: declare-os em neon.ts e execute neon deploy --env <arquivo>. <arquivo> é o arquivo ignorado pelo git que o env pull já escreve (.env se esse arquivo existir, caso contrário .env.local). Env pull escreve apenas variáveis gerenciadas pelo Neon; adicione segredos da Function a esse arquivo. Todas as chaves de Function env declaradas devem estar presentes. Omita uma chave de neon.ts se você não quiser escrevê-la. undefined significa que você solicitou escrever a chave e o valor está ausente (defineConfig lança um erro). Nunca converta um valor process.env ausente para string vazia: isso faria upload de "" e deletaria a chave ativa. Uma atribuição vazia no arquivo (KEY=) também é "". Se TypeScript precisar de uma asserção, use process.env.X!.
...
[truncado para tradução]