QueryDeck Docs
CLI

ORM detection

What qdeck reads from each ORM config, how environment variables are resolved, and what to do when detection misses.

For: developers.

qdeck ships with seven ORM detectors plus a .env fallback. This page documents what each one reads, how environment variable resolution works, and the limits of automatic detection.

Detection order

When you run qdeck in a project, detectors run in this order:

  1. Prisma
  2. Drizzle
  3. TypeORM
  4. Knex
  5. Sequelize
  6. Django
  7. Rails
  8. .env fallback (only if none of the above match)

The first match wins. Run with --verbose to see which one matched and which files were inspected.

Prisma

PropertyValue
Config filesschema.prisma, prisma.config.ts, prisma.config.js
Search pathprisma/, project root

What it reads

For classic schema.prisma:

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

qdeck reads the provider and the url. If url is env("VAR"), the value is resolved from .env files.

For Prisma v6+ with prisma.config.ts or .js:

export default {
  datasourceUrl: process.env.DATABASE_URL,
} satisfies PrismaConfig

The datasourceUrl field and process.env.X patterns are supported. Direct URL strings (url: "postgres://...") also work.

Drizzle

PropertyValue
Config filesdrizzle.config.ts, drizzle.config.js, drizzle.config.mjs
Search pathProject root

What it reads

import { defineConfig } from "drizzle-kit";

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

Both the inline credentials form (host, port, user, password, database) and the url form are supported. dialect maps to the engine type.

TypeORM

PropertyValue
Config filesdata-source.ts, data-source.js, ormconfig.json
Search pathProject root, src/

What it reads

For data-source.ts:

export const AppDataSource = new DataSource({
  type: "postgres",
  host: process.env.DB_HOST,
  port: 5432,
  username: process.env.DB_USER,
  password: process.env.DB_PASS,
  database: "myapp",
});

The detector pulls type, host, port, username, password, database. process.env.X values are resolved from .env files.

For ormconfig.json, the same fields apply with JSON syntax.

Knex

PropertyValue
Config filesknexfile.js, knexfile.ts, knexfile.mjs
Search pathProject root

What it reads

Knex configs are environment-keyed:

module.exports = {
  development: {
    client: "pg",
    connection: process.env.DATABASE_URL,
  },
  production: {
    client: "pg",
    connection: {
      host: process.env.DB_HOST,
      user: process.env.DB_USER,
      password: process.env.DB_PASS,
      database: "myapp_prod",
    },
  },
};

qdeck reads the environment based on NODE_ENV. If NODE_ENV is not set, it defaults to development. client maps to the engine type (pg, postgresql, mysql, mysql2, sqlite3).

Sequelize

PropertyValue
Config filesconfig/config.json, config/config.js, .sequelizerc
Search pathProject root, config/

What it reads

Like Knex, Sequelize configs are environment-keyed:

{
  "development": {
    "username": "root",
    "password": null,
    "database": "myapp_dev",
    "host": "127.0.0.1",
    "dialect": "postgres"
  }
}

dialect maps to the engine type. NODE_ENV selects the environment.

Django

PropertyValue
Config filessettings.py, settings/*.py modules
Search pathProject root, <project_name>/, config/, settings/

What it reads

DATABASES = {
    "default": {
        "ENGINE": "django.db.backends.postgresql",
        "NAME": "myapp",
        "USER": os.environ.get("DB_USER"),
        "PASSWORD": os.environ.get("DB_PASSWORD"),
        "HOST": "localhost",
        "PORT": "5432",
    }
}

The default connection is used. ENGINE maps:

  • django.db.backends.postgresql → PostgreSQL
  • django.db.backends.mysql → MySQL
  • django.db.backends.sqlite3 → SQLite

os.environ.get("X"), os.getenv("X"), and env("X") patterns are resolved from .env files (if django-environ or similar is in use).

If you have multiple settings files (e.g., settings/development.py and settings/production.py), qdeck looks at the one matching DJANGO_SETTINGS_MODULE, falling back to settings.py.

Rails

PropertyValue
Config filesconfig/database.yml
Search pathProject root, config/

What it reads

default: &default
  adapter: postgresql
  encoding: unicode
  pool: 5

development:
  <<: *default
  database: myapp_development
  username: <%= ENV["DB_USER"] %>
  password: <%= ENV["DB_PASS"] %>
  host: localhost

qdeck reads the environment matching RAILS_ENV (or RACK_ENV). If neither is set, it defaults to development. ERB tags <%= ENV["X"] %> are resolved from .env files (if dotenv-rails is in use).

YAML aliases (<<: *default) are expanded automatically.

.env fallback

If no ORM matches, qdeck scans these files in order:

  1. .env
  2. .env.local
  3. .env.development
  4. .env.production

For each file, it looks for variables holding a database URL:

VariableFormat
DATABASE_URLMost common
DB_URL, POSTGRES_URL, POSTGRESQL_URL, MYSQL_URL, PG_URL, PG_CONNECTION_STRINGCommon alternatives

The first URL found is used. If you have multiple databases, the fallback picks the first one. Use a direct URL instead for explicit control:

qdeck postgres://user:pass@host/db

Environment variable resolution

qdeck reads .env files using a permissive parser:

  • Quoted and unquoted values supported.
  • Comments (#) stripped.
  • Multi-line values not supported.
  • ${VAR} interpolation supported.
  • .env.local overrides .env.
  • .env.<NODE_ENV> overrides both, if NODE_ENV matches.

If a variable isn't found in any .env file, qdeck falls back to the actual process environment. If still not found, detection fails with an error pointing at the missing variable.

Environment classification

After detection, qdeck classifies the connection as local, staging, or production based on the host:

Host patternClassified as
localhost, 127.0.0.1, 0.0.0.0, *.local, host.docker.internallocal
Hostnames containing staging, stage, dev, qa, teststaging
Everything elseproduction

The classification is used to set the connection color in QueryDeck (green/orange/red). You can change it after the fact in the connection editor.

When detection misses

If detection finds the wrong database, or no database at all:

  1. Run qdeck --verbose --dry-run to see what was inspected.
  2. If the issue is in a specific file, fix the config or .env.
  3. Use a direct URL: qdeck postgres://user:pass@host/db — no detection involved.
  4. File a bug via the in-app feedback button. Include the redacted config we should detect.

What's next