Insights and Resources

How to use a serverless SQL database for edge functions

A serverless SQL database for edge functions scales to zero, speaks HTTP, and places data near the function runtime. Learn how it works, compare providers, and set up Telnyx SQL Database with TypeScript.

[Resource] Serverless SQL Database for Edge Functions feature image

Takeaways

  • A serverless SQL database for edge functions scales to zero, speaks HTTP instead of persistent TCP, and places data near the function runtime.
  • Edge runtimes cannot hold TCP connection pools, so standard Postgres and MySQL clients do not work inside them.
  • Geography is the bigger cost. A function in Sydney querying a database in Virginia pays a physical latency floor on every request.
  • Cloudflare D1, Neon, Turso, PlanetScale, and Supabase each solve part of this. Telnyx runs the function and the SQL layer on one owned network.
  • A full setup walkthrough follows, with working TypeScript, CLI commands, and the limits to plan around.

What is a serverless SQL database for edge functions?

A serverless SQL database for edge functions is a relational database that provisions on demand, scales to zero when idle, is reachable over HTTP rather than a long-lived TCP socket, and places its data close to where the function runtime executes. The last part is what separates it from a normal managed database with a serverless price tag.

The distinction matters because edge functions run in constrained runtimes distributed across many locations. They are optimized to start in single-digit milliseconds and to run near the request. A database designed for a long-lived application server assumes the opposite: a stable process, a connection pool held open for hours, and a network path measured in single-digit milliseconds because the app server sits in the same datacenter.

Pair the two naively and you get a distributed frontend bolted onto a single-region backend. The function starts fast, then waits. If you want the foundational concept first, our serverless database guide covers the scaling and billing model before you get to placement.

Three things define the category:

  1. HTTP or WebSocket access. No pg over raw TCP, because the runtime will not allow it.
  2. Scale to zero. No always-on compute charge for a database that serves bursty traffic.
  3. Deliberate data placement. Either replicas near the runtime or a primary on the same network as the compute.

Run your database where your functions runTelnyx Edge Compute and SQL Databases live on the same owned network. No cross-vendor hops, no per-query latency floor. Explore Edge Compute or read the SQL Database docs.

Start building free

Why traditional serverless databases fall short at the edge

Two separate failure modes get conflated. Solving one does not solve the other.

Connection management breaks first

PostgreSQL implements a process per user client and server model, where every client connection spawns a dedicated backend process. That design is why connection pooling exists. Reusing a socket across thousands of requests amortizes an expensive setup.

Edge runtimes cannot participate. Vercel's Edge runtime is built on V8 isolates that expose a subset of Web APIs and no filesystem. Cloudflare Workers, Deno Deploy, and Supabase Edge Functions land in the same place. An invocation cannot hold a socket open for the next one.

Vendors answered with HTTP drivers. The Neon serverless driver queries Postgres over HTTP or WebSockets instead of TCP, and PlanetScale supports the same driver on platforms where TCP pooling is not viable. This works. It is a real fix for a real problem.

Geography is the failure mode nobody patched

An HTTP driver changes the transport. It does not move the data.

Network latency has a floor set by physics. Light in fiber travels at roughly two thirds of its vacuum speed, which puts the round trip from New York to Sydney at about 160 milliseconds along an idealized great-circle path. Real routes run higher once you add routing, queuing, and processing delay at every hop.

Now put a function in Sydney and a Postgres primary in Virginia. Your function executes in 5ms and waits 200ms. Run an ORM that issues four sequential queries to assemble one response and you have spent nearly a second before the first byte leaves. This is not a code optimization problem. Regional serverless functions avoid it by sitting next to the database on purpose, which is a sound trade and also the thing edge deployment was supposed to remove.

Supabase says this out loud. Its documentation on Edge Functions tells you to treat Postgres as a remote pooled service and to pin execution to the database region for data-heavy work. That is correct engineering advice, and it is also an admission that the compute moved and the data did not.

How edge compute and serverless SQL work together on one network

The structural fix is to stop treating compute and storage as two purchases from two vendors with a public internet segment between them.

Telnyx Edge Compute runs functions as containers on infrastructure Telnyx owns, at edge sites inside carrier facilities rather than generic cloud availability zones. Telnyx SQL Databases are SQLite databases that live on that same platform. You declare one in your function manifest and it resolves as env.DB at runtime, with credentials injected by the platform rather than written into your code.

Here is the honest architecture, because the details determine whether this fits your workload.

Each SQL database is served by one primary, and statements run one at a time against it. There are no regional read replicas. The gain is not that a copy of your data sits in every city. The gain is that the function runtime, the SQL layer, the object storage, the GPU inference, and the telephony network are all on one owned backbone under one bill, so the path between your handler and your rows is short and does not leave that network.

The consistency story is simpler as a result. A row written on the request path is immediately visible to the next CLI query or REST call, because every access path hits the same primary. There is no replica lag to reason about, no read-your-writes edge case to defend against, and no session token to thread through your code.

When you need true zero-hop access, the platform has a second surface. Stateful edge functions built on Stateful Actors, currently in beta, embed a private SQLite database inside each actor instance, reached synchronously through ctx.storage.sql. That database cannot be queried from outside its actor and cannot join across instances, which is the trade you make for having no network between code and data.

Serverless SQL databases for edge functions compared

ProviderEngineData placementEdge access modelBest fit
Telnyx SQL DatabaseSQLiteOne primary on Telnyx networkenv.DB binding, REST, CLICompute, SQL, and telephony on one platform
Cloudflare D1SQLitePrimary plus read replicasWorker binding, REST via control planeRead-heavy apps already on Workers
NeonPostgreSQLSingle primary regionHTTP and WebSocket driverTeams committed to full Postgres
TursolibSQLCloud primary plus local replicasHTTP client, embedded replica fileServers and devices with a filesystem
PlanetScaleMySQL on Vitess, PostgreSQLRegional primary, read replicasNeon serverless driver, HyperdriveLarge scale and schema migrations
SupabasePostgreSQLSingle project regionPooled connection or HTTP driverBundled backend with auth and storage

Vendor limits and pricing move quickly. Every figure below reflects each provider's published documentation as of August 2026, and it is worth confirming against their live docs before you commit.

Cloudflare D1 is the incumbent and deserves the position. Read replication made the read-heavy case stronger, and the free tier is hard to beat. The constraints are published: 10 GB per database on the paid plan, 100 columns per table, 100 bound parameters per query, and single-threaded query processing, so a workload averaging 1ms per query tops out near 1,000 queries per second. Binding access is a Workers feature. A REST API exists, but it routes through Cloudflare's control plane and costs meaningfully more latency. If those limits are what brought you here, our roundup of Cloudflare D1 alternatives goes deeper on migration paths.

Neon gives you real PostgreSQL with extensions, branching, and scale-to-zero, plus the driver that made edge Postgres viable at all. Its database still lives in one primary region, so an edge function in another hemisphere pays the distance.

Turso is architecturally the closest thing on this list to local data access, and it deserves a fair hearing. Embedded replicas keep a SQLite file inside your application process and serve reads in microseconds. One caveat matters for this use case, and Turso documents it: serverless environments without a filesystem cannot use embedded replicas. That rules out the isolate-based edge runtimes this article focuses on. From a Worker or a Vercel edge function you are calling Turso's cloud primary over HTTP like any other remote database.

PlanetScale brings Vitess-grade horizontal scaling and the best schema migration workflow in the category. It is not an edge product. Its own documentation routes Cloudflare Workers users to Hyperdrive and Vercel users to PgBouncer, which tells you where the data is expected to live.

Supabase is a backend, not just a database, and the bundle is genuinely convenient. Its edge functions and its Postgres instance are not co-located, so the round trip persists inside a single vendor.

How to connect an edge function to Telnyx SQL Database

Five steps, start to finish. You need the telnyx-edge CLI authenticated and Node.js installed.

Step 1: Create the database

telnyx-edge storage sqldb create --name my-app-db

The response carries a UUID. That id is the only durable handle, since names are not resolution keys. A new database starts at status: "pending" and takes roughly 2 to 11 seconds to provision. Poll until status reads provision_ok rather than sleeping a fixed interval, because SQL sent to a pending database returns a 409.

telnyx-edge storage sqldb get 550e8400-e29b-41d4-a716-446655440000

Step 2: Create the schema

Schema can exist before any code does. The --remote flag is required, as there is no local emulation.

telnyx-edge storage sqldb execute 550e8400-e29b-41d4-a716-446655440000 --remote \
  --command "CREATE TABLE links (
    id INTEGER PRIMARY KEY,
    slug TEXT NOT NULL UNIQUE,
    target TEXT NOT NULL,
    created_at TEXT NOT NULL DEFAULT (datetime('now'))
  )"

Add an index on anything you filter or order by. Statements against one database run one at a time, so a slow scan delays everything queued behind it.

telnyx-edge storage sqldb execute 550e8400-e29b-41d4-a716-446655440000 --remote \
  --command "CREATE INDEX IF NOT EXISTS links_by_slug ON links(slug)"

Use execute for one-off statements and inspection. For schema changes you intend to keep and replay on another database, use migrations instead: numbered .sql files applied in order with telnyx-edge storage sqldb migrations, tracked by a table inside the database itself.

Step 3: Bind the database to a function

Scaffold a function first, then add the storage block to the manifest.

telnyx-edge new-func -n links-api -l ts
npm install @telnyx/edge-runtime
name = "links-api"
main = "src/index.ts"
compatibility_date = "2026-05-14"

[storage.sqldb.DB]
id = "550e8400-e29b-41d4-a716-446655440000"

[edge_compute]
func_id = "<written by new-func>"
func_name = "links-api"

The block name, DB here, becomes env.DB in your code. Bind by id, not by name. Then generate types, which runs offline and needs no authentication.

telnyx-edge types

Step 4: Query the database from the function

One thing trips people up. The binding lives on the env you import from @telnyx/edge-runtime, not on the second argument to fetch(req, env). Reading it off that argument compiles cleanly and is undefined at runtime.

// env is imported, not passed to fetch
import { env } from "@telnyx/edge-runtime";

type Link = { id: number; slug: string; target: string };

export default {
  async fetch(req: Request): Promise<Response> {
    if (req.method === "POST") {
      const { slug, target } = (await req.json()) as { slug: string; target: string };

      const inserted = await env.DB
        .prepare(`INSERT INTO links (slug, target) VALUES (?, ?) RETURNING id`)
        .bind(slug, target)
        .run<{ id: number }>();

      return Response.json({ id: inserted.results[0]!.id }, { status: 201 });
    }

    const slug = new URL(req.url).searchParams.get("slug");

    const { results } = await env.DB
      .prepare(`SELECT id, slug, target FROM links WHERE slug = ?`)
      .bind(slug)
      .all<Link>();

    return Response.json({ links: results });
  },
};

Note the shape. prepare() is synchronous and returns a statement, .bind() returns a new statement rather than mutating the original, and the terminal calls are async. Always bind request values with ? placeholders instead of concatenating SQL.

Step 5: Ship and verify

telnyx-edge ship

ship prints the function host. Call it, then read the same rows back from your terminal to confirm both paths reach the same primary.

B=https://links-api-<func-id-prefix>.telnyxcompute.com

curl -sS -X POST $B -H 'content-type: application/json' \
  -d '{"slug":"docs","target":"https://developers.telnyx.com"}'

telnyx-edge storage sqldb execute 550e8400-e29b-41d4-a716-446655440000 --remote \
  --command "SELECT id, slug, target FROM links ORDER BY id DESC LIMIT 5"

Telnyx publishes typical figures to set expectations. Under typical conditions, a warm simple statement runs about a 10ms round trip, and a 5,000-row read takes roughly 160ms. Treat both as guidance rather than a guarantee, since query shape, index coverage, and result size all move the number. Plan around the documented ceilings too: 1 GiB per database, 2 MiB per bound value, and a result set capped near 4 MiB per statement. Page through large reads rather than selecting them whole, and keep images and archives in object storage with the key in SQL.

When to use same-network SQL versus a centralized serverless database

Neither answer is universally right, and the honest framing helps you more than a pitch.

Reach for SQL on the same network as your compute when your traffic is globally distributed and latency sensitive, your workload is read-heavy with modest write concurrency, your dataset fits comfortably under a gigabyte per database, and you want one vendor for compute, storage, and observability. Real-time voice and agent workloads fit this profile especially well, because every avoidable hop competes with the inference budget for the same conversational turn.

Stay with a centralized serverless database when you need PostgreSQL extensions, stored procedures, or a rich type system, your writes are heavily transactional and concurrent, your dataset runs to hundreds of gigabytes, or your team already has migration tooling, dashboards, and runbooks built around Postgres. SQLite has an honest published guide to its own limits, including one writer per database at a time and a recommendation against direct simultaneous access over a network. Read it before you commit either way.

Factor in data residency early, not late. Where your rows physically sit is a compliance question as much as a latency one. Under GDPR and similar regimes, moving personal data across borders is a decision your legal team needs to sign off on, and retrofitting placement after launch is expensive. This is where the architecture cuts both ways. A replicated edge database spreads copies of your data across jurisdictions by design, which is excellent for read latency and awkward for sovereignty. A single primary keeps the data in one place you can point to on a map, which is easier to attest to and slower for distant readers. Telnyx runs edge compute sites across North America, Europe, APAC, and MENA, and processes in-region traffic with in-region compute as a property of how the network is built rather than a configuration flag, so the data boundary follows the traffic. Whichever provider you choose, confirm the placement guarantees in writing before you migrate production data.

Two adjacent choices are worth naming. Vector search for retrieval workloads belongs in a purpose-built embeddings database rather than a relational one, even when the relational data sits nearby. And if you are still choosing a compute layer, decide that first, because the compute decision constrains the database decision far more than the reverse.

Frequently asked questions

Can edge functions connect to a SQL database?
Yes, but not over raw TCP. Edge runtimes block persistent TCP sockets, so you connect through an HTTP driver, a WebSocket driver, or a platform binding that the runtime resolves for you. Neon, PlanetScale, Turso, and Cloudflare D1 all offer an HTTP path, and Telnyx SQL Databases resolve as env.DB from the function manifest.
What database works with Cloudflare Workers?
D1 is the native option, reached through a Worker binding. Workers also connect to Neon, Turso, PlanetScale, and Supabase over HTTP drivers, and Hyperdrive pools connections to external Postgres and MySQL databases. The trade is that only D1 gets binding-speed access, while the rest are network calls to another provider.
Why do TCP database connections fail in edge runtimes?
Most edge runtimes execute JavaScript in V8 isolates rather than containers, and an isolate is not a long-lived process with its own network stack. It cannot hold a socket open between invocations, so a connection pool has nothing to pool. Vendors work around this by exposing the database over HTTP or WebSockets.
What is the difference between an edge database and a serverless database?
Serverless describes the billing and scaling model, meaning no provisioned instance and no charge when idle. Edge describes data placement, meaning the data sits near where code runs rather than in one central region. A database can be serverless and still centralized, which is why the two terms are not interchangeable.
Is SQLite good enough for production edge workloads?

For most of them, yes. SQLite handles unlimited concurrent readers and one writer at a time, which suits read-heavy request paths, session lookups, feature flags, counters, and per-entity state. It is the wrong choice for heavy concurrent writes, datasets in the hundreds of gigabytes, or workloads that need Postgres extensions.

Bring your data to where your functions run

An edge function that waits on a database in another hemisphere is not an edge application. Telnyx runs functions, SQL, key-value storage, object storage, GPU inference, and the carrier network on one owned platform, so the rows your handler needs are already on the network your handler runs on.

Sign up for free and create your first database with one CLI command, or talk to our team if you want to walk through the architecture before you build.

Share on Social
Eli Mogul
Eli Mogul
Content Writer & Editor

Eli is the content writer and editor at Telnyx. Born and raised in Chicago, Eli attended the University of Missouri where he obtained a BA in Journalism. Eli joined Telnyx in August of 2025. In his spare time, you'll find Eli reading, playing video games, or running.