neon-functions

Long-running, serverless Node.js HTTP functions deployed onto your Neon branch, with DATABASE_URL injected automatically and compute that runs next to your data. Use when a user wants to host an API, an AI agent with long streaming responses, a WebSocket or server-sent-events (SSE) server, a webhook

By neondatabase · 5,611 installs

npx skills add neondatabase/agent-skills --skill neon-functions

Source repository · Upstream listing

FIRST : Use the parent neon skill for a Neon overview, getting started with Neon, Neon development best practices, and more. If the neon skill is not installed, fetch it from https://neon.com/docs/ai/skills/neon/SKILL.md or install it with: Neon Functions This is a public beta feature, currently available in us east 2 and eu central 1 . Neon Functions are long running Node.js HTTP handlers deployed onto a Neon branch. Each function gets a public HTTPS URL, runs in the same region as your database, and — if the branch has Postgres — gets DATABASE URL injected automatically. You deploy and manage them through the same Neon CLI, neon.ts , and API you already use. Use this skill to help the user define, run locally, deploy, and manage functions next to their database. Deliver a deployed function with its invocation URL, a working local neon dev loop, or a precise answer from the official Neon docs. When to Use Reach for Neon Functions when the workload is a request/response handler that benefits from staying alive and staying close to the data: Long running request/response flows that outlast lambda style limits. Agents that make several LLM calls and tool invocations per request, or image/video generation, routinely blow past the ~10–60s execution caps and short streaming windows of traditional serverless functions. Neon Functions are long running: the handler just needs to start responding within 15 minutes, and an open stream stays alive as long as bytes keep flowing. That's enough headroom for real agent workloads. Stateful streaming without bolting on Redis. Because a function stays alive across a request, it can host an SSE endpoint or a WebSocket server and hold the connection open in process — no external state store (Redis, etc.) needed just to keep a stream coherent. Module scope state (a pg pool, an in memory counter) persists across requests on the same isolate. Compute that must sit next to Postgres. The function runs in the same region as the branch's database, so there are no cross region round trips on every query. DATABASE URL is injected for you. A backend that branches with your data. Each branch runs its own version of the function at its own URL, against its own isolated database (and storage, and gateway) state. Preview deployments, CI, and dev environments each get a self contained backend — deploying to a child never affects the parent. Webhooks, bots, and post response work. Webhook handlers that fan out into multiple DB writes, Discord/WebSocket bots, and fire and forget follow ups via waitUntil (analytics, audit logs) all fit. If the workload is a pure static site, a cron/background job that needs its own lifecycle and cancellation, or something that must run outside the supported regions ( us east 2 , eu central 1 ) today, this isn't the right tool yet (see [Timeouts and Runtime Limits]( timeouts and runtime limits) and [Availability]( availability)). What It Does Long running & serverless — Built for WebSocket servers (see [WebSocket Servers]( websocket servers)), SSE endpoints (see [Server Sent Events (SSE)]( server sent events sse)), long agent HTTP streams, and APIs. Still scales to zero when idle. Web standard handler — A function is any default export with a fetch(request) method returning a Response (Workers/WinterTC compatible). A Hono app exports exactly that shape, so export default app just works. Runs on Node.js 24, so all Node APIs are available. Close to your database — Runs in the branch's region; DATABASE URL injected automatically when the branch has Postgres. Branchable — Each branch runs its own function version at its own URL against its own isolated state. Same CLI/API — Deploy and manage via neon , neon.ts , or the Neon API. Availability Check this precondition before setting anything up: Neon Functions is a public beta feature currently available in us east 2 and eu central 1 . Confirm the user's Neon project is in one of these regions. Functions usage isn't billed during the public beta. Architecture: Where Functions Fit Neon (Functions included) is backend primitives, not full stack app hosting . Host your app on Vercel (or Netlify, or another frontend/app host); Functions are the long running, stateful slice of your backend that lives next to your data. They compose with that platform in two ways: Add a Function to a full stack app. Your Next.js / TanStack Start app on Vercel (or Netlify) owns UI, auth (e.g. Neon Auth), and talks directly to Lakebase Postgres and Object Storage. When one workload outgrows the host's short serverless limits — a WebSocket or SSE server, or a long running agent that would time out — move just that piece onto a Neon Function. (See [Functions as an Agent Backend]( functions as an agent backend nextjs and similar frameworks) for the client direct pattern.) Run the whole backend control plane on Functions. Especially when the frontend is client only — TanStack Router, React Router in client mode, and similar SPAs hosted on Vercel or Netlify — the client calls Functions directly . Build REST APIs and request/response agents, host MCP servers , and run anything stateful or that belongs close to Postgres and Object Storage. Either way, secure a Function like any standalone REST API: verify a JWT or API key at the top of the handler (see the WARNING under [Functions as an Agent Backend]( functions as an agent backend nextjs and similar frameworks)). Because a Function is just your backend, you can move pieces between your host and Neon — relocate an agent or a stateful WebSocket server onto a Function when it needs more runtime, and back if needed. Setup Functions are declared in neon.ts (see the neon skill for the branch first workflow and neon.ts basics). Add @neon/config and declare functions under preview.functions , keyed by slug : The slug is the function's permanent identity (it appears in the invocation URL and CLI commands) and can't be changed after the first deploy. Use name for a human readable label. A minimal function — a Hono app that queries the branch's Postgres via the injected DATABASE URL : Create the pg pool at module scope (reused across requests on the same isolate) and keep max small (e.g. 5), since each isolate keeps its own pool. Call attachDatabasePool(pool) so an idle disconnect is not an uncaughtException — see [Connecting to Postgres]( connecting to postgres). parseEnv(config) requires every variable the config implies. A function that only talks to Postgres over the pooled URL can scope it to just that key — parseEnv then validates and returns only what you asked for (the keys autocomplete from your neon.ts ): Develop Locally and Deploy Keep .env or .env.local up to date with every key under preview.functions. .env . neon env pull writes Neon managed vars only; add Function secrets to that file, then pass it as env . neon deploy env <file loads that file into process.env each time, then uploads those values. A missing value is undefined and defineConfig throws. Omit the key from neon.ts if you do not want to write it. Never coerce a missing process.env value to an empty string (that uploads "" and deletes the live key). An empty assignment ( KEY= ) is also "" . Use process.env.X! when TypeScript needs an assertion. To deploy a single function without applying neon.ts : neon functions deploy <slug src src/index.ts ( src takes either the entry file or a directory containing index.ts , index.mjs , or index.js ). That command's env is KEY=VALUE (repeatable), not a file path. Use it for a targeted env update. Retrieve the public URL with neon functions get <slug (the invocation url field, of the form https://<branch id <slug .compute.<cell .us east 2.aws.neon.tech ). Manage with neon functions list get delete . When neon checkout creates a new branch and a neon.ts is present, it applies the policy automatically. That create apply does not load env . If Function env reads process.env , run neon deploy env <file after checkout (add update existing if checkout already created the branch). Checking out an existing branch does not re deploy; run neon deploy env <file explicitly. Neon Infrastructure as Code ( neon.ts ) The preview.functions block from [Setup]( setup) is part of neon.ts , Neon's infrastructure as code file — one TypeScript file declares every function (its source , display name , and env ) alongside any other branch services, in version control (see the neon skill for the full reference). Treat it like Terraform for your branch: Functions are branch scoped : each branch runs its own deployment at its own URL. When a neon.ts is present, neon checkout applies the policy as it creates a branch. That create apply does not load env . If Function env reads process.env , run neon deploy env <file after checkout. Checking out an existing branch doesn't redeploy — run neon deploy env <file to apply changes. Per branch deploy tuning (e.g. runtime ) lives in the branch closure, keyed by slug, so it can vary by branch without changing which functions exist: Environment Variables Neon injects branch scoped connection strings and service URLs at runtime — you don't declare these or pass them at deploy time: Variable Notes NEON BRANCH The branch name (e.g. main , preview/foo ). Injected on every branch, including the default. DATABASE URL Pooled connection string. Use for most queries. Present only if the branch has Postgres. DATABASE URL UNPOOLED Direct connection. Use for migrations, LISTEN / NOTIFY , multi round trip transactions. NEON AUTH BASE URL Present when Neon Auth is enabled on the branch. NEON DATA API URL Present when the Data API is enabled on the branch. Object storage ( AWS ) and AI Gateway ( NEON AI GATEWAY ) vars are also injected when those services are declared — see the neon object storage and neon ai gateway skills. neon env pull / neon env run / neon dev emit NEON BRANCH (and the connection strings) into your local dev environment too, so local runs mirror the deployed runtime. Your own secrets are per deployment. Preferred path: declare them in neon.ts and run neon deploy env <file . <file is the gitignored file env pull already writes ( .env if that file exists, otherwise .env.local ). Env pull writes Neon managed vars only; add Function secrets to that file. All declared Function env keys must be present. Omit a key from neon.ts if you do not want to write it. undefined means you asked to write the key and the value is missing ( defineConfig throws). Never coerce a missing process.env value to an empty string: that uploads "" and deletes the live key. An empty assignment in the file ( KEY= ) is also "" . If TypeScript needs an assertion, use process.env.X! and make sure the file has the value: neon functions deploy env KEY=VALUE is the manual path (repeatable; env KEY= deletes a key; unmentioned keys carry over). Use it for a targeted env update, not a full neon.ts apply. Load Function secrets into the same file env pull wrote, then neon deploy env <file . Pull the branch's Neon managed vars onto disk for local dev with neon env pull ( link / checkout do this automatically; pass no env pull to skip and use neon env run <cmd for runtime injection). Limits: ≤1,000 vars, ≤64 KiB total, and the NEON prefix is reserved. Connecting to Postgres When the branch has Postgres, Neon injects the connection strings