◆ 慣れてくると結構使いやすい

以前は sql`SELECT ${variable}` みたいにテンプレートリテラルで書けるものを自作して使ってましたが 世間で使われてるライブラリのほうがいいかなとか思って最近は Node.js の DB アクセス系ツールで一番人気らしい knex を使ってました

基本的なことをするには簡単に使えて便利ですが困ったところもありました

then で実行

以前 Short の方でも書いた内容ですが 実行メソッドが then なんです
Promise 的に考えて then ですること無いなら書かなくていいやと思って書かないと実行されません

knex("table").insert({value: 1})
knex("table").insert({value: 2}).then(result => {})

だと value が 2 のほうしか insert は行われません

await するか async 関数で return すれば内部的に then メソッドが呼び出されるので 何もしない then を書きたくないならこっちもありです

async function knexInsert1() {
await knex("table").insert({value: 3})
}

async function knexInsert2() {
return knex("table").insert({value: 4})
}

async 関数じゃない普通の関数だと return しても then は呼び出されません

insert で空配列の場合

こっちも Short に書いた内容ですが knex では更新がない場合には結果が空配列になります
ただし insert に空配列を渡した場合はクエリが空っぽになるので何も実行されず 結果も knex の特別なオブジェクトです

insert select を使えば insert クエリを実行してかつ結果が 0 件になりますが 書き方が特別になるのでそういうことをするくらいなら事前に空配列ならその場で 空配列を return するみたいな関数を通したほうが簡単です

const insertRows = async values => knex("table").insert(values).returning("*")

// ⇩

const insertRows = async values => (!values || values.length === 0) ? [] : knex("table").insert(values).returning("*")

NOTIFY/LISTEN 非対応

PostgreSQL の NOTIFY/LISTEN 機能を使おうとしたら knex は非対応でした
knex は PostgreSQL 以外も対応してるから PostgreSQL 独自の NOTIFY/LISTEN 機能には対応しないようです

NOTIFY の ? に非対応

上の通り NOTIFY/LISTEN は非対応で notify などのメソッドはありません
なら raw で送信すればいいかと思ってやってみました

> knex = require("knex")({ client: "pg", connection: { database: "test1", user: "postgres" } })
> knex.raw("NOTIFY ch, ?", [JSON.stringify({x: 1})]).toQuery()
`NOTIFY ch, '{"x":1}'`

問題なく SQL が作れています
送信してみると

> knex.raw("NOTIFY ch, ?", [JSON.stringify({x: 1})]).then(console.log)
Unhandled rejection error: "$1"またはその近辺で構文エラー

構文エラーになりました
NOTIFY の payload に 「?」 は対応していないようです
これは knex というより pg ドライバや PostgreSQL 自体が対応していないからみたいです

SELECT 句で pg_notify 関数を使えばできるみたいです
https://github.com/brianc/node-postgres/issues/1258

toQuery で得られる SQL は正しいものなのでそれを raw で送ることもできます

> const sql = knex.raw("NOTIFY ch, ?", [JSON.stringify({x: 1})]).toQuery()
> knex.raw(sql).then(console.log)
Result { ... }

なぜか sqlite3 のエラー

PostgreSQL しか使ってないのになぜかエラーメッセージに sqlite3 が出て困りました
https://github.com/knex/knex/issues/2441

原因は未設定の knex を使ったことでした

const knex = require("knex")
const config = require("./config")("pgsql")
const pg = knex({ client: "pg", connection: config })

pg("foo")
.whereIn(
"id",
knex("bar").where("num", ">", 100)
)
).select("*")

こういう感じで 設定済みが pg で require したものが knex という変数に入っていて pg を使わないといけないのにコピペのせいで knex が混ざってました
こういうことを防ぐために ドキュメントなどでいったん knex という名前で require したものを保持していないのかもです
ただ config の取得が非同期になったりで pg を作るのが後回しになることもあったり 設定が違うインスタンスを別に作りたいケースもあったりなので 気をつけるしか無いですね

使ってない sqlite3 周りのエラーメッセージなら未設定の knex を使ってるかもとおぼえておけばそこまで困らず対処できます

returning の結果

returning を使うと insert などで更新された行のデータを取得できます
これまでは

knex("table").insert(values).returning("*")
knex("table").insert(values).returning(["id", "name", "ts", "user_id"])

みたいな指定をしていて オブジェクトの配列を結果として取得できていました
必要な返り値が id だけだったので

knex("table").insert(values).returning("id")

と書いたところ 返り値のフォーマットが異なっていて困りました
id の数値の配列になっていました

配列で取得するには要素が 1 つでも配列として渡さないとダメなようです
例外的に 「*」 は全部の列を表すので文字列でも オブジェクトの配列で取得できていました