From 538d0a148871406e0cd28e0a15af445e5c9d1e9e Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Wed, 1 Apr 2026 13:03:11 +0200 Subject: [PATCH] cli: enable embedded-postgres via config instead of env var Rather than requiring the `EXPERIMENTAL_DEV_DB` environment variable, the embedded postgres server is now started automatically when `backend.database.client` is set to `embedded-postgres` in the app config. The CLI reads the config before spawning the backend and injects the actual pg connection details via env override. Signed-off-by: Patrik Oldsberg Made-with: Cursor --- .changeset/thin-elephants-joke.md | 5 ++- packages/backend-defaults/config.d.ts | 2 +- .../commands/package/start/startBackend.ts | 3 ++ .../src/lib/runner/runBackend.ts | 37 ++++++++++++++++++- 4 files changed, 42 insertions(+), 5 deletions(-) diff --git a/.changeset/thin-elephants-joke.md b/.changeset/thin-elephants-joke.md index e64940551b..a7ed28bc9e 100644 --- a/.changeset/thin-elephants-joke.md +++ b/.changeset/thin-elephants-joke.md @@ -1,5 +1,6 @@ --- -'@backstage/cli': patch +'@backstage/cli-module-build': patch +'@backstage/backend-defaults': patch --- -The `package start` command now supports an experimental `EXPERIMENTAL_DEV_DB` env flag that can be set to enable the use of `embedded-postgres` as the database for local development, rather than SQLite. For this to work the desired version of the `embedded-postgres` package must be installed in your project, typically as a `devDependency`. +Added experimental support for using `embedded-postgres` as the database for local development. Set `backend.database.client` to `embedded-postgres` in your app config to enable this. The `embedded-postgres` package must be installed as an explicit dependency in your project. diff --git a/packages/backend-defaults/config.d.ts b/packages/backend-defaults/config.d.ts index 3d09146b0d..40352c5972 100644 --- a/packages/backend-defaults/config.d.ts +++ b/packages/backend-defaults/config.d.ts @@ -585,7 +585,7 @@ export interface Config { /** Database connection configuration, select base database type using the `client` field */ database: { /** Default database client to use */ - client: 'better-sqlite3' | 'sqlite3' | 'pg'; + client: 'better-sqlite3' | 'sqlite3' | 'pg' | 'embedded-postgres'; /** * Base database connection string, or object with individual connection properties * @visibility secret diff --git a/packages/cli-module-build/src/commands/package/start/startBackend.ts b/packages/cli-module-build/src/commands/package/start/startBackend.ts index a36a93b8ff..7b71e52da3 100644 --- a/packages/cli-module-build/src/commands/package/start/startBackend.ts +++ b/packages/cli-module-build/src/commands/package/start/startBackend.ts @@ -23,6 +23,7 @@ import { runBackend } from '../../../lib/runner'; interface StartBackendOptions { targetDir: string; checksEnabled: boolean; + configPaths?: string[]; inspectEnabled?: boolean | string; inspectBrkEnabled?: boolean | string; linkedWorkspace?: string; @@ -33,6 +34,7 @@ export async function startBackend(options: StartBackendOptions) { const waitForExit = await runBackend({ targetDir: options.targetDir, entry: 'src/index', + configPaths: options.configPaths, inspectEnabled: options.inspectEnabled, inspectBrkEnabled: options.inspectBrkEnabled, linkedWorkspace: options.linkedWorkspace, @@ -56,6 +58,7 @@ export async function startBackendPlugin(options: StartBackendOptions) { const waitForExit = await runBackend({ targetDir: options.targetDir, entry: 'dev/index', + configPaths: options.configPaths, inspectEnabled: options.inspectEnabled, inspectBrkEnabled: options.inspectBrkEnabled, require: options.require, diff --git a/packages/cli-module-build/src/lib/runner/runBackend.ts b/packages/cli-module-build/src/lib/runner/runBackend.ts index 4c64c5154e..c0c88e42db 100644 --- a/packages/cli-module-build/src/lib/runner/runBackend.ts +++ b/packages/cli-module-build/src/lib/runner/runBackend.ts @@ -20,8 +20,13 @@ import { ctrlc } from 'ctrlc-windows'; import { IpcServer, ServerDataStore } from '../ipc'; import debounce from 'lodash/debounce'; import { fileURLToPath } from 'node:url'; -import { isAbsolute as isAbsolutePath } from 'node:path'; +import { + isAbsolute as isAbsolutePath, + resolve as resolvePath, +} from 'node:path'; import { targetPaths } from '@backstage/cli-common'; +import { ConfigSources } from '@backstage/config-loader'; +import { ConfigReader } from '@backstage/config'; import spawn from 'cross-spawn'; import { startEmbeddedDb } from './startEmbeddedDb'; @@ -46,6 +51,8 @@ export type RunBackendOptions = { require?: string | string[]; /** An external linked workspace to override module resolution towards */ linkedWorkspace?: string; + /** Config file paths from --config flags */ + configPaths?: string[]; }; export async function runBackend(options: RunBackendOptions) { @@ -60,7 +67,8 @@ export async function runBackend(options: RunBackendOptions) { const extraEnv: Record = {}; - if (process.env.EXPERIMENTAL_DEV_DB) { + const dbClient = await readDatabaseClient(options.configPaths); + if (dbClient === 'embedded-postgres') { const db = await startEmbeddedDb(); extraEnv.APP_CONFIG_backend_database = JSON.stringify({ client: 'pg', @@ -207,3 +215,28 @@ export async function runBackend(options: RunBackendOptions) { return () => exitPromise; } + +async function readDatabaseClient( + configPaths?: string[], +): Promise { + const rootDir = targetPaths.rootDir; + const source = ConfigSources.default({ + rootDir, + allowMissingDefaultConfig: true, + argv: (configPaths ?? []).flatMap(p => [ + '--config', + resolvePath(rootDir, p), + ]), + }); + + const abortController = new AbortController(); + for await (const { configs } of source.readConfigData({ + signal: abortController.signal, + })) { + abortController.abort(); + return ConfigReader.fromConfigs(configs).getOptionalString( + 'backend.database.client', + ); + } + return undefined; +}