Database hosting & serverless data providers
DaloyJS doesn't ship a database. It runs on Node, Bun, Deno, Cloudflare Workers, AWS Lambda, Vercel, Netlify, Azure Functions, and Fastly Compute, so the right database depends on which adapter you target.
The pages in this section cover where your data lives: managed hosts and their drivers, separately from how you query it (see ORMs and ODMs). Most providers here pair with Drizzle or Prisma rather than replacing them.
How each runtime reaches the database
Long-lived servers (Node, Bun, Deno) and Node-based serverless functions (Lambda, Vercel, Netlify, Azure Functions) open ordinary TCP connections, so the standard pg and mysql2 drivers work there.
Cloudflare Workers can open outbound TCP too, through the connect() API from cloudflare:sockets, and pg (8.16.3 or later, per Cloudflare's Postgres tutorial) and mysql2 run on Workers with the nodejs_compat flag. A Worker cannot keep a connection open between requests, though, so every request would pay a fresh TCP, TLS and auth handshake to a database that may be far away. Cloudflare recommends putting Hyperdrive in front of Postgres or MySQL: it keeps a warm connection pool near the database and can cache reads. The other two options on Workers are an HTTP / WebSocket driver (Neon, PlanetScale MySQL, Turso, Supabase) and a Workers binding (D1).
Supported providers
- Neon: serverless Postgres with branching, scale-to-zero, and an HTTP/WebSocket driver (
@neondatabase/serverless). Also reachable withpgover TCP or through Hyperdrive. - PlanetScale: managed MySQL on Vitess with an HTTP driver (
@planetscale/database), plus PlanetScale Postgres, which you reach with standard Postgres drivers. - Supabase: hosted Postgres plus auth, storage, and realtime via the fetch-based
@supabase/supabase-js. - Turso: libSQL (SQLite fork) via
@libsql/client, over HTTP on edge runtimes. - DuckDB: embedded OLAP SQL engine for analytics, local files, Parquet, and ad hoc reporting inside Node.js services.
- Cloudflare D1: SQLite-compatible database built into Workers, accessed through a runtime binding.
- AWS Aurora DSQL: distributed PostgreSQL on AWS with IAM token auth, a natural fit for the Lambda adapter.
- Azure databases: Azure Database for PostgreSQL (
pg), Azure SQL Database (mssql), and Cosmos DB (@azure/cosmos) with managed identity and Microsoft Entra tokens instead of passwords, for the Azure Functions adapter or Container Apps. - Cloudflare Hyperdrive: not a database but a pooling and caching layer that lets a Worker use
pgormysql2against any of the Postgres or MySQL hosts above (and RDS, Aurora, or your own server).
Runtime compatibility
"Yes" means the provider's documented driver runs there. Vercel, Netlify, and Azure Functions run Node.js by default, so Node-compatible drivers are expected to work, but the combinations marked "Not verified" have not been tested for this guide. Fastly Compute is a WebAssembly runtime with no TCP and outbound fetch only to configured backends.
| Provider | Driver style | Node.js | Bun / Deno | Cloudflare Workers | AWS Lambda | Vercel | Netlify | Azure Functions | Fastly Compute |
|---|---|---|---|---|---|---|---|---|---|
| Neon | HTTP / WebSocket, or TCP (pg) | Yes | Yes | Yes (HTTP, or TCP via Hyperdrive) | Yes | Not verified | Not verified | Not verified | Not verified |
| PlanetScale MySQL | HTTP (@planetscale/database) | Yes | Yes | Yes (HTTP, or TCP via Hyperdrive) | Yes | Not verified | Not verified | Not verified | Not verified |
| PlanetScale Postgres | TCP (pg) | Yes | Yes | Yes (TCP via Hyperdrive) | Yes | Not verified | Not verified | Not verified | No |
| Supabase | fetch-based | Yes | Yes | Yes (HTTP) | Yes | Not verified | Not verified | Not verified | Not verified |
| Turso (libSQL) | HTTP / WebSocket | Yes | Yes | Yes (HTTP) | Yes | Not verified | Not verified | Not verified | Not verified |
| DuckDB | embedded native library | Yes | Limited | No | Yes | Not verified | Not verified | Not verified | No |
| Cloudflare D1 | Workers binding | REST API, or local dev via Wrangler | REST API only | Yes (binding) | REST API only | REST API only | REST API only | REST API only | REST API only |
| Aurora DSQL | TCP (pg) + IAM token | Yes | Not verified | Not covered by this guide | Yes | Not verified | Not verified | Not verified | No |
| Azure Database for PostgreSQL | TCP (pg) + Entra token | Yes | Not verified | Not verified (Hyperdrive needs a password role) | Not verified | Not verified | Not verified | Yes | No |
| Azure SQL Database | TCP (mssql, tedious) + Entra token | Yes | Not verified | Not verified | Not verified | Not verified | Not verified | Yes | No |
| Azure Cosmos DB for NoSQL | HTTPS (@azure/cosmos) + Entra token | Yes | Not verified | Not verified | Not verified | Not verified | Not verified | Yes | Not verified |
| Any Postgres / MySQL | TCP (pg, mysql2) | Yes | Yes | Yes (TCP via Hyperdrive) | Yes | Not verified | Not verified | Not verified | No |
The D1 REST API is meant for administration and tooling, not for serving application traffic from outside Workers.
Choosing one
- You target Cloudflare Workers exclusively: D1 (built-in), any Postgres or MySQL through Hyperdrive, or Neon / PlanetScale / Turso over HTTP.
- You want MySQL with database branching: PlanetScale.
- You want auth + storage + realtime in one package: Supabase.
- You're all-in on AWS with the Lambda adapter: Aurora DSQL or RDS Postgres via standard
pg. - You're on Azure Functions or Container Apps: Azure Database for PostgreSQL, Azure SQL, or Cosmos DB with a managed identity.
- You need SQLite-style data close to users: Turso or D1.
- You need embedded analytics over files or local data: DuckDB on Node.js or Lambda.
Connection lifecycle
How you open and close connections depends on whether the process outlives the request.
- Long-lived servers. Create one pool per process at startup,
app.decorate()it, and close it withapp.onClose().onClose()andonShutdown()run only on the Node, Bun, and Deno adapters, after in-flight requests drain. See Lifecycle & health. - Serverless functions (Lambda, Vercel, Netlify, Azure Functions). There is no shutdown hook to rely on: a warm container is frozen and later discarded without notice. Create the pool at module scope so warm invocations reuse it, keep
maxsmall (each concurrent instance holds its own connections), set an idle timeout so frozen connections are dropped, and set a maximum connection lifetime below any server-side limit (Aurora DSQL closes connections after 60 minutes). Prefer the provider's pooled endpoint (Neon-pooler, PlanetScale PgBouncer on port 6432, RDS Proxy) when there is one. - Cloudflare Workers. A socket cannot be shared across requests. Use an HTTP driver or a D1 binding, create and close a TCP client inside the request, or let Hyperdrive hold the pool for you.
Readiness checks for the database
app.readinesscheck() reports 503 while the app is draining or a plugin is still registering. Pass checks to also gate it on the database: each check gets an AbortSignal, the checks run concurrently, and a check that throws, rejects, returns false, or runs past checkTimeoutMs (default 2000 ms) fails the probe with 503. The body stays { status: "not-ready" }; the failure details are logged server-side only.
Keep the checks cheap, and point only the readiness probe at /readyz, not the liveness probe: a database outage should take an instance out of rotation, not restart every replica at once. See Lifecycle & health.
Credentials
Keep connection strings and tokens out of the repository and out of logs. On Workers, store them with pnpm wrangler secret put DATABASE_URL (or use a Hyperdrive binding, which keeps the credentials in the Hyperdrive config). On AWS, read them from Secrets Manager or SSM Parameter Store at cold start, or skip passwords entirely with IAM auth (Aurora DSQL, RDS IAM). Never echo driver errors to clients: they can contain hostnames, usernames, and SQL. DaloyJS redacts 5xx detail in production, so let unexpected errors throw and log them server-side.
Region placement
Latency between the function and the database usually dominates. Put serverless functions in the same region as the primary database (Vercel and Lambda let you pick it), use read replicas for far-away readers, and on Workers combine Hyperdrive with Smart Placement so the Worker runs near the database instead of near the user when it makes several queries per request.
Local development
Run Postgres or MySQL in Docker for TCP drivers, use a Neon or PlanetScale development branch, turso dev or a local SQLite file for libSQL, and wrangler dev for D1 (local SQLite) and Hyperdrive (localConnectionString). Keep the same driver and schema in development as in production so dialect differences surface early.
Backups and branching
Every managed host here has point-in-time restore or snapshots (D1 calls it Time Travel), and Neon and PlanetScale also offer database branches, which pair well with preview deployments. Check the retention window on your plan before you depend on it, and test a restore at least once.
Migrations
Run migrations from CI or a one-off task with a privileged role, not from the request path, and let the app connect with a role that can only read and write data. Use the tooling of your query layer: Drizzle Kit, Prisma Migrate, wrangler d1 migrations for D1, and PlanetScale deploy requests for PlanetScale MySQL.
Once you've picked a host, layer your query API on top: Drizzle ORM works with every provider above except DuckDB, and Prisma works with most of them through driver adapters.