# How the `getDatabaseProvider` Function Works in OpenSEO: Database Backend Selection

> Learn how OpenSEO's getDatabaseProvider selects PostgreSQL or Cloudflare D1 by reading the DATABASE_PROVIDER environment variable. Optimize your database backend configuration today.

- Repository: [Every App/open-seo](https://github.com/every-app/open-seo)
- Tags: internals
- Published: 2026-09-02

---

**The `getDatabaseProvider` function in OpenSEO reads the `DATABASE_PROVIDER` environment variable to determine whether to use PostgreSQL via Hyperdrive or Cloudflare D1, returning `"postgres"` or `"d1"` respectively while throwing an error for unsupported configurations.**

OpenSEO is an open-source SEO management platform built for Cloudflare Workers. The `getDatabaseProvider` function serves as the single source of truth for database backend selection, allowing the application to switch seamlessly between PostgreSQL and Cloudflare's D1 SQLite without hardcoding environment checks throughout the codebase.

## Where `getDatabaseProvider` is Implemented

### Source Location and Imports

The function lives in **[`src/db/provider.ts`](https://github.com/every-app/open-seo/blob/main/src/db/provider.ts)**. It imports the Cloudflare Workers runtime environment to access configuration variables securely.

```typescript
import { env } from "cloudflare:workers";

export function getDatabaseProvider(): "postgres" | "d1" {
  const provider = Reflect.get(env, "DATABASE_PROVIDER");
  
  if (provider === "postgres") return "postgres";
  if (provider === "d1" || provider === undefined || provider === "") {
    return "d1";
  }
  
  throw new Error(`Unsupported DATABASE_PROVIDER: ${provider}`);
}

```

### The Decision Logic

The function uses **`Reflect.get(env, "DATABASE_PROVIDER")`** to retrieve the configuration value. This approach safely accesses the binding without assuming its existence at compile time. The logic follows a strict priority:

1. **Exact match `"postgres"`** – Returns `"postgres"` to enable PostgreSQL mode
2. **`"d1"`, `undefined`, or empty string** – Falls back to `"d1"` for Cloudflare's SQLite offering
3. **Any other value** – Throws a descriptive error to prevent startup with invalid configurations

## How Database Selection Works at Runtime

### Reading the Cloudflare Workers Environment

OpenSEO runs on Cloudflare Workers, where environment variables and bindings are injected via the `env` object. The `getDatabaseProvider` function accesses this runtime context directly through the `cloudflare:workers` module import. You can set `DATABASE_PROVIDER` in your [`wrangler.toml`](https://github.com/every-app/open-seo/blob/main/wrangler.toml) configuration or via a `.env` file during local development.

### Supported Database Providers

The function explicitly supports two backends:

- **`"postgres"`** – Triggers PostgreSQL connectivity through the Hyperdrive binding, suitable for high-performance production workloads
- **`"d1"`** – Activates Cloudflare's distributed SQLite database, ideal for edge-deployed applications with lower latency requirements

If the variable is omitted entirely, the function defaults to `"d1"` to ensure out-of-the-box functionality with Cloudflare's native infrastructure.

### Error Handling for Invalid Configurations

Unlike silent defaults that might mask typos, `getDatabaseProvider` validates inputs strictly. Setting `DATABASE_PROVIDER="mysql"` or any unsupported string immediately throws an exception with the message `Unsupported DATABASE_PROVIDER: [value]`, preventing runtime ambiguity.

## Helper Function: `getPostgresConnectionString`

When the provider resolves to `"postgres"`, the companion function **`getPostgresConnectionString`** constructs the connection string from the `HYPERDRIVE` binding. This separation of concerns keeps provider detection independent from connection management.

```typescript
import { getPostgresConnectionString } from "@/db/provider";

const connectionString = getPostgresConnectionString();
const pool = new Pool({ connectionString });

```

The Hyperdrive binding automatically handles connection pooling and latency optimization between your Worker and the PostgreSQL instance.

## Usage Examples in OpenSEO

### Branching Database Logic in Services

Application services call `getDatabaseProvider()` to execute database-specific queries without importing environment details:

```typescript
import { getDatabaseProvider } from "@/db/provider";

if (getDatabaseProvider() === "postgres") {
  await pgClient.query('SELECT * FROM seo_data WHERE url = $1', [url]);
} else {
  await d1DB.prepare('SELECT * FROM seo_data WHERE url = ?').bind(url).run();
}

```

### Configuring Telemetry Backends

The telemetry system uses the provider to tag metrics correctly:

```typescript
import { getDatabaseProvider } from "@/db/provider";

const telemetryDeps = {
  getDbBackend: getDatabaseProvider,
  // ... other dependencies
};

```

## Integration Across the Codebase

The `getDatabaseProvider` function integrates into multiple critical paths:

- **[`src/db/index.ts`](https://github.com/every-app/open-seo/blob/main/src/db/index.ts)** – Instantiates the correct `db` object based on the provider value
- **[`src/lib/auth.ts`](https://github.com/every-app/open-seo/blob/main/src/lib/auth.ts)** – Adjusts authentication queries for PostgreSQL syntax versus D1 constraints
- **[`src/db/runBatch.ts`](https://github.com/every-app/open-seo/blob/main/src/db/runBatch.ts)** – Executes batch operations using provider-specific transaction handling
- **[`src/server/lib/self-host-telemetry.ts`](https://github.com/every-app/open-seo/blob/main/src/server/lib/self-host-telemetry.ts)** – Reports which backend is active for monitoring purposes

This centralized architecture ensures that adding a third database backend requires changes only in [`src/db/provider.ts`](https://github.com/every-app/open-seo/blob/main/src/db/provider.ts) and the database initialization layer.

## Summary

- The `getDatabaseProvider` function in **[`src/db/provider.ts`](https://github.com/every-app/open-seo/blob/main/src/db/provider.ts)** determines the active database by inspecting `env.DATABASE_PROVIDER`
- It returns `"postgres"` for PostgreSQL via Hyperdrive or `"d1"` (the default) for Cloudflare's SQLite
- Invalid values trigger immediate exceptions to prevent misconfiguration
- **`getPostgresConnectionString`** handles connection string generation when PostgreSQL mode is active
- Downstream files like **[`src/db/index.ts`](https://github.com/every-app/open-seo/blob/main/src/db/index.ts)** and **[`src/lib/auth.ts`](https://github.com/every-app/open-seo/blob/main/src/lib/auth.ts)** consume this function to maintain environment-agnostic business logic

## Frequently Asked Questions

### What happens if `DATABASE_PROVIDER` is not set?

If the variable is undefined or empty, `getDatabaseProvider` defaults to returning `"d1"`, enabling the application to run immediately on Cloudflare's D1 infrastructure without explicit configuration.

### Can I use a database other than PostgreSQL or D1?

No. The function explicitly validates the provider value and throws an error for any unsupported database. To add support for another backend like MySQL, you must modify [`src/db/provider.ts`](https://github.com/every-app/open-seo/blob/main/src/db/provider.ts) to recognize the new provider string and update all-consuming services to handle the new connection type.

### How does `getPostgresConnectionString` work with Hyperdrive?

The function accesses the `HYPERDRIVE` binding from the Cloudflare Workers environment, which contains pre-configured connection details for your PostgreSQL instance. It returns a formatted connection string that Node.js PostgreSQL clients like `pg` can consume directly, with Hyperdrive managing connection pooling and latency optimization automatically.

### Where should I configure `DATABASE_PROVIDER` in a Cloudflare Workers environment?

Set the variable in your [`wrangler.toml`](https://github.com/every-app/open-seo/blob/main/wrangler.toml) file under `[vars]` for production deployments, or create a `.env` file in your project root for local development with `wrangler dev`. The Cloudflare Workers runtime injects these values into the `env` object at runtime.