◆ where は空オブジェクトを入れると何も追加しない
◆ select は 0 以外の false とみなせる値なら何も追加しない
  ◆ 複数の列を追加する場合には使えず最初の引数のみ
◆ 複雑になってくると if メソッドを追加したほうがいいかも

メソッドチェーンにしないなら

knex で SQL を作る時 where や select を条件によって入れたり入れなかったりしたいことがあります
メソッドチェーンにしないなら あとから if 文の中で各メソッドを呼び出せばできます

const builder = knex("table")
if (id) {
builder.where("id", id)
}
console.log(builder.toString())

メソッドチェーンにする

メソッドチェーンの流れで書きたくて その分岐のためだけに一旦途切れさせたくないです

if メソッドみたいなメソッドチェーンを便利にしてくれる機能があればよいのですが 特にそういったものはなさそうです
ライブラリ自体を拡張するのは避けたかったので デフォルトの機能でできないか いろいろ試してみると こういう書き方にすればできました

const id = null // or 1, 20, ...
const get_column1 = false // or true

const b = knex("table")
.where(
id ? { id } : {}
)
.select("id")
.select(
get_column1 && "column1"
)

console.log(b.toString())

where

まず where ですが 単純に null や undefined を入れてもエラーになったり意図しないクエリになります
何もしない動きは空オブジェクトを入れることで作れます
そのため 条件を満たすならオブジェクト形式で条件を書き 条件を満たさないなら空オブジェクトになるようにします

.where(
id ? { id } : {}
)

ちょっと複雑になりますが 引数を 3 つ渡すタイプで where を指定するにはこういう書き方もできます

.where(
...(
id
? ["id", ">", id]
: [{}]
)
)

where の場合は SQL の条件をグループ化するためにコールバック関数を使うこともできるのでこれを利用することもできます

.where(sub => {
if (id) {
sub.where("id", id)
}
})

SQL を出力したときにコールバック内の builder で作った部分は () で囲まれています

select

select の方は単純です
null などの 0 以外の false とみなせる値が渡されると何もしないので

.select(
need_id ? "id" : null
)

で済みます
ただし 可変長引数で複数の列名を書く場合は使えません
途中に null があると , だけ余分に表示されて構文エラーですし undefined や false は文字列として select 句に追加されてしまいます
追加しないかもしれないものは別の select に分けて書く必要があります

knex を拡張する

select と where ならこれで済みましたが join はメソッドチェーンに含まれるのに何もしないというのは簡単にはできなそうでした
また 条件を満たしたときに追加したい列が JOIN した先のテーブルにあったりすると join と select をひとつのまとまりで追加したいときもあります
そうなってくるとやっぱり メソッドチェーンをせずに if 文を使うしかないです

しかし こういうスッキリしたメソッドチェーンの結果を返すだけの関数という作りを維持したいです

const getById = async id =>
knex("table")
.where("id", id)
.select("id", "name")
.first()

仕方ないので if メソッドを追加することにしました

Knex.QueryBuilder.extend("if", function(cond, cb) {
if (cond) cb(this)
return this
})

client や connection を設定して作るインスタンスではなく import したオブジェクトの QueryBuilder.extend メソッドでメソッドを追加できます
継承した新しいクラスが返ってくるものではなく 既存のものに追加という形です
なのでインスタンス固有ではなくグローバルなものになります

上の extend を実行した後だとこういう風に if が書けます

knex("table")
.where("id", ">", 1)
.if(true, b => {
b.where("id", "<", 10)
})
.toString()
select * from "table" where "id" > 1 and "id" < 10

knex("table")
.where("id", ">", 1)
.if(false, b => {
b.where("id", "<", 10)
})
.toString()
select * from "table" where "id" > 1