Skip to content

ORM and Framework Guides

Every ORM and web framework connects to a pgEdge Starfleet database the same way: over a standard Postgres connection string. What differs by framework is where it reads that string, why sslmode=require at the end of it matters, and how a generated migration processes CREATE EXTENSION.

Nothing in a pgEdge Starfleet connection string is unique to pgEdge, so you do not need an adapter, driver patch, or extra package to connect.

Getting the Connection String

Use the connection string on the Application tab of your database's Connect pane, which displays connection details for the app role. The Connect pane also has an Admin tab, with credentials for the admin role; later sections use it to install extensions app cannot. app owns the database; since an object belongs to its creating role, a framework connected as app owns every table its migrations create and can alter or drop them later.

Each example that follows reads the connection string from DATABASE_URL; for security, put it there via a secrets mechanism rather than an exported shell variable. Single-quote the value if you write it into an env file, since a password may contain $, and a double-quoted value is expanded by the shell that sources the file.

Keep the whole string, including the options. The console always appends sslmode=require, and a URI trimmed back to its host and database silently drops that setting. pgEdge Starfleet hosts serve TLS with a valid, CA-signed certificate, so require works from every client, though you may prefer a stricter mode.

The console provides a URL, not discrete PG* values. A framework that requires separate host, port, user, and password parameters (such as Django) must split the string into its components, and the Connect pane displays each part on its own row.

Checking the String with psql

Using the Connect pane's psql command block is the quickest way to test the string before implementing a framework. A row returned by a SELECT version() query confirms that the host details resolve, the TLS handshake completes, and the role can authenticate with the Postgres server. If a framework fails when the psql check succeeds, the problem lies in its own configuration rather than in the database. See Connecting with psql.

Extensions in Migrations

Most ORM migrations include CREATE EXTENSION IF NOT EXISTS pgcrypto, which on pgEdge Starfleet succeeds when run as app, and app then owns the extension and can drop it in a later migration. The reverse is true for vector, which only admin can install.

Neither role is a superuser. Most supported extensions, such as pgcrypto, citext, or hstore, install as app. A few, such as vector or postgis, install as admin only, and app is refused with Must be superuser to create this extension. See Installing Supported Extensions on a pgEdge Starfleet Managed Database for the full table, refusal messages, and install order.

A migration run with the Application tab's string installs pgcrypto successfully, since that string connects as app. A migration that also needs an admin-only extension such as vector or postgis fails, because app cannot install it; connect with the Admin tab's credentials and install that extension manually first.

Prisma

Prisma reads the URL from the datasource block in schema.prisma. By default, that block already points at an environment variable:

datasource db {
  provider = "postgresql"
  url      = env("DATABASE_URL")
}

Set DATABASE_URL to the string the console provided. Prisma's default is sslmode=prefer, which allows a plain-text connection when TLS is unavailable, so the sslmode=require on the end of the console's string keeps the connection encrypted.

The Prisma PostgreSQL connector reference lists the other arguments Prisma reads from the query string.

A Prisma migration that runs CREATE EXTENSION pgcrypto works against the Application tab's string. A migration that runs CREATE EXTENSION vector does not.

Drizzle

Drizzle connects through the pg driver, which parses the URI itself, so the string passes directly into the constructor:

import { drizzle } from 'drizzle-orm/node-postgres';

const db = drizzle(process.env.DATABASE_URL);

Drizzle Kit keeps its own copy of the connection string for migrations, under dbCredentials in drizzle.config.ts:

import { defineConfig } from 'drizzle-kit';

export default defineConfig({
  dialect: 'postgresql',
  dbCredentials: { url: process.env.DATABASE_URL },
});

Both Drizzle and Drizzle Kit read the same variable, so one env file covers the application and the migration tool, and both connect as app. The Drizzle Postgres guide describes the driver alternatives. A Drizzle migration containing CREATE EXTENSION pgcrypto works with that shared DATABASE_URL. Connect with the Admin tab's credentials to install an admin-only extension first.

Django

Django reads discrete parameters from the DATABASES setting rather than a URL, so the string must be split or parsed. The split version reads the parts of the console's string from the standard PG* environment variables, which every libpq client also honors:

DATABASES = {
    "default": {
        "ENGINE": "django.db.backends.postgresql",
        "NAME": os.environ["PGDATABASE"],
        "USER": os.environ["PGUSER"],
        "PASSWORD": os.environ["PGPASSWORD"],
        "HOST": os.environ["PGHOST"],
        "PORT": os.environ["PGPORT"],
        "OPTIONS": {"sslmode": "require"},
    }
}

Django's Postgres backend passes OPTIONS to the driver's connection constructor; the TLS setting therefore sits inside OPTIONS rather than beside the host.

Keep the sslmode entry, because it is the split-parameter form of the sslmode=require the console appends.

The alternative is dj-database-url, which parses a URI into the same dictionary and reads DATABASE_URL by default:

import dj_database_url

DATABASES = {"default": dj_database_url.config()}

The Django databases reference describes the remaining DATABASES options Django's Postgres backend accepts. A Django migration whose operations include CREATE EXTENSION pgcrypto runs as app and succeeds. Connect with the Admin tab's credentials to install an admin-only extension first.

Ruby on Rails

Active Record reads DATABASE_URL from the environment with no configuration, so that variable and an empty config/database.yml are enough to connect. A url key in the YAML takes precedence over the variable.

Reading the variable through ERB binds one environment to one connection without committing the string:

production:
  url: <%= ENV['DATABASE_URL'] %>

The Rails configuration guide describes how the two sources are merged. A Rails migration that enables pgcrypto runs as app and succeeds. A migration that enables an admin-only extension does not, so connect with the Admin tab's credentials and install it before running db:migrate.

SQLAlchemy and Alembic

SQLAlchemy builds an engine from the URI directly:

import os
from sqlalchemy import create_engine

engine = create_engine(os.environ["DATABASE_URL"])

Alembic reads the URL from the sqlalchemy.url key of alembic.ini, a file most projects commit, and a live password does not belong in a committed file.

Set the value at run time from env.py instead:

import os
from alembic import context

context.config.set_main_option(
    "sqlalchemy.url", os.environ["DATABASE_URL"])

The Alembic tutorial describes the rest of that file. An Alembic revision issuing CREATE EXTENSION pgcrypto runs as app and succeeds. Connect with the Admin tab's credentials to install an admin-only extension first.

After a Password Rotation

An application's in-use connection string no longer functions when someone selects Rotate credentials on the Application tab of the Connect pane. The new password does not authenticate until the database returns to Available, and the old one may still work in that window, so switch the application over when the status displays Available rather than immediately.

Rotating the app password also restarts the database's MCP Server when its Allow writes setting is on. The server reads the password once at startup.