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.
typescript
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_URL and SUPABASE_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.
typescript
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_URL and UPSTASH_REDIS_REST_TOKEN — shown on the database's REST API card.
typescript
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_URL and TURSO_AUTH_TOKEN — create the token with turso db tokens create.
typescript
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.

Related Documentation