Connect to a database
Functions run on an edge runtime with fetch() but no raw TCP sockets, so classic drivers like pg or mysql2 cannot open connections. HTTP-based serverless drivers are purpose-built for this and work perfectly.
Step-by-Step Guide
1.How it works
npm driver imports are bundled at deploy time by the platform (esbuild), so import { neon } from "@neondatabase/serverless" just works in the editor — no package.json needed. The drivers below talk to their databases over HTTPS, which is exactly what the runtime provides.
- TCP drivers (
pg,mysql2,ioredis) will not work — they need raw sockets the runtime does not provide. - Egress and subrequest limits apply per execution (free tier: 1MB egress, 20 subrequests) — see Limits and Plans.
2.Neon (serverless Postgres)
Neon's serverless driver runs tagged-template SQL queries over HTTPS. Template: “Neon Postgres CRUD”.
- Secret:
NEON_DATABASE_URL— copy it from the Neon dashboard → Connection Details.
import { secret } from "@hostfunc/sdk";import { neon } from "@neondatabase/serverless";export async function main(input: { limit?: number }) { const sql = neon(await secret.getRequired("NEON_DATABASE_URL")); const items = await sql`SELECT id, title FROM items ORDER BY created_at DESC LIMIT ${input.limit ?? 10}`; return { items };}3.Supabase
The Supabase client talks to PostgREST over fetch, so reads and writes work without sockets. Template: “Supabase todos”.
- Secrets:
SUPABASE_URLandSUPABASE_ANON_KEY— both are under project Settings → API. - New tables have Row Level Security enabled — add policies that allow the anon key, or disable RLS for demo tables.
import { secret } from "@hostfunc/sdk";import { createClient } from "@supabase/supabase-js";export async function main() { const supabase = createClient( await secret.getRequired("SUPABASE_URL"), await secret.getRequired("SUPABASE_ANON_KEY"), ); const { data, error } = await supabase.from("todos").select("*").limit(20); if (error) throw new Error(error.message); return { todos: data };}4.Upstash Redis
Upstash exposes Redis over a REST API, so every command is a fetch call. Template: “Upstash Redis cache”.
- Secrets:
UPSTASH_REDIS_REST_URLandUPSTASH_REDIS_REST_TOKEN— shown on the database's REST API card.
import { secret } from "@hostfunc/sdk";import { Redis } from "@upstash/redis";export async function main(input: { key?: string }) { const redis = new Redis({ url: await secret.getRequired("UPSTASH_REDIS_REST_URL"), token: await secret.getRequired("UPSTASH_REDIS_REST_TOKEN"), }); const hits = await redis.incr(`hits:${input.key ?? "home"}`); return { hits };}5.Turso (libSQL)
Turso needs zero dependencies — send raw fetch requests to <url>/v2/pipeline with a bearer token. Template: “Turso (libSQL) events”.
- Secrets:
TURSO_DATABASE_URLandTURSO_AUTH_TOKEN— create the token withturso db tokens create.
import { secret } from "@hostfunc/sdk";export async function main() { const url = await secret.getRequired("TURSO_DATABASE_URL"); const token = await secret.getRequired("TURSO_AUTH_TOKEN"); const res = await fetch(`${url}/v2/pipeline`, { method: "POST", headers: { authorization: `Bearer ${token}`, "content-type": "application/json" }, body: JSON.stringify({ requests: [ { type: "execute", stmt: { sql: "SELECT datetime('now') AS now" } }, { type: "close" }, ], }), }); return await res.json();}6.When you don't need a database
Counters, form submissions, and caches fit comfortably in the built-in per-function KV storage — no account, no secrets, no connection string. Reach for an external database when you need relational queries, shared data across functions, or more than the per-function key limit.