ReviewOS

stacks/bun-query-builder

How to Specify Database Settings

#1001
Closed djamez opened this 2 months ago ·
2 months ago

Hello

I stumbled upon this project and having used Eloquent, Knex, Kysely and Objection; and also bun's SQL;, I do think - from the documentation - that this presents a really nice DX that draws from the afore-mentioned libraries.

My attempt to test it has been unsuccessful. I cannot seem to be able to specify database settings.

I tried this:

config.dialect = 'mysql';
config.sql.randomFunction = 'RAND()';
config.sql.sharedLockSyntax = 'LOCK IN SHARE MODE';
config.sql.jsonContainsMode = 'function';

config.database.url = "mysql://root:password@localhost:3306/testdb";

...
const db = createQueryBuilder(...);

const ok = await db.ping()
await db.waitForReady({ attempts: 100, delayMs: 250 })

..yields "Database not ready after waiting".

Attempting to use the longer version:

config.database.host = 'localhost';
config.database.port = 3306;
config.database.username = 'root';
config.database.password = 'password';
config.database.database = 'testdb';

...yields the same error. The documentation does not mention this anywhere - or at least I could not find it. Not even in the examples.

Please advise.

PS: The documentation on the website (https://bun-query-builder.netlify.app) is lagging behind the repo quite significantly.

9 days ago

I have revisited the library and found out that if I remove await db.waitForReady({ attempts: 100, delayMs: 250 }) everything else works fine.

9 days ago

Thanks for the report, and sorry it took a while — your follow-up ("everything works if I remove waitForReady") was the clue that made this findable. There were two separate bugs here, and you hit both.

1. Why ping() returned false on the version you tried

The lazy connection proxy was built on a non-callable target, so the tagged-template form — sql`SELECT 1`, which is exactly what ping() uses — threw "not a function", while other code paths that went through .unsafe() kept working. That is precisely the shape you saw: every query fine, ping()/waitForReady() alone failing. Fixed in #1035. I re-ran your exact flow (mutate config.database.url, then build, then ping) against current main and it returns true.

2. A second bug, still live until now

ping() and waitForReady() health-checked the module-global connection rather than the connection your builder actually queries. So they could report a healthy builder as down, or — more commonly — report ready at boot while the first real query fails, because the usual boot order is:

export const db = createQueryBuilder({ schema })  // captures the connection HERE
await getConfig()                                 // ...config lands after

Fixed in #1065, along with a much more serious relative of the same bug: writes issued inside db.transaction() were running on a different pooled session and surviving the rollback.

Working configuration

Config must be set before createQueryBuilder(), since the builder captures its connection at construction. Either form works:

import { createQueryBuilder, setConfig } from 'bun-query-builder'

setConfig({
  dialect: 'mysql',
  database: { url: 'mysql://root:password@localhost:3306/testdb' },
})

// or the discrete form — but pick one or the other, not both:
// database: { host: 'localhost', port: 3306, username: 'root', password: 'password', database: 'testdb' }

const db = createQueryBuilder({ schema })

Or a config file, which is the route I'd recommend now:

// query-builder.config.ts
import { defineConfig } from 'bun-query-builder'

export default defineConfig({
  dialect: 'mysql',
  database: { url: 'mysql://root:password@localhost:3306/testdb' },
})
await getConfig() // once at boot, BEFORE createQueryBuilder()

Two things worth knowing:

  • database.url and the discrete host/port/username/password fields are mutually exclusive — when url is set it is used verbatim and the others are ignored. A stale url sitting in a config file silently beating your explicit credentials is a trap; #1064 made the config-file veto section-level so that can't happen.
  • Only DB_CONNECTION, DB_DIALECT, DB_DATABASE, DB_NAME and DB_SSL are actually read from the environment. DB_HOST, DB_PORT, DB_USERNAME, DB_PASSWORD are not, despite appearing in some docs — that's a real docs bug and it's on the list.

On the docs

You were right on both counts. Connection configuration genuinely was not documented — that is fixed in #1064, which adds a configuration section to the README and docs/config.md and gives you defineConfig so there is a copy-pasteable starting point. The wider audit your report prompted also turned up ~80 documented calls to methods that don't exist (db.update, db.delete, db.insert, db.execute, …) and six pages corrupted mid-code-block; those are being fixed separately.

Closing this as diagnosed — the original cause is long fixed and the second is in #1065. Please reopen if a correct configuration still fails for you on MySQL specifically; I verified the code path on Postgres and SQLite but did not have a MySQL server to hand.

Sign in to comment on this issue.