この記事は何か
denoでローカルネットワーク上のSQL Serverに接続して問合せする、
ただそれだけのために結構つまづきました。
あまり需要が無いかもしれませんが、成功した実装を紹介します。
結論
以下のコードで接続と問い合わせができました。
コメント内の(1)などは、後述の解説と対応します。
import "https://deno.land/std@0.193.0/dotenv/load.ts"; //(2)
import {
Disposable,
using,
} from "https://deno.land/x/disposable@v1.1.1/mod.ts"; //(4)
//@deno-types="npm:@types/mssql" //(1)
import mssql from "npm:mssql@9.1.1"; //(1)
//(2)
const conPool = new mssql.ConnectionPool({
user: Deno.env.get("DB_USER"),
password: Deno.env.get("DB_PASSWORD"),
server: Deno.env.get("DB_SERVER") as string,
database: Deno.env.get("DB_DATABASE"),
//(3)
options: {
//denoの場合、npmのtlsが使えないため false にする必要あり。
encrypt: false,
//これをfalseにしないと、問合せ結果の日時がUTCになってしまう。
useUTC: false,
},
});
//(4)
await using(
(await conPool.connect()) as mssql.ConnectionPool & Disposable,
async (con) => {
con.dispose = con.close;
const dbReq = con.request();
const query = `SELECT GETDATE() AS DBDATE;`;
const rs = await dbReq.query(query);
console.log(rs.recordset[0]["DBDATE"].constructor.name); //→ Date
console.log(rs.recordset[0]["DBDATE"]); //ex→ Fri Aug 11 2023 22:42:27 GMT+0900 (日本標準時)
}
);
DB_USER=(接続ユーザー)
DB_PASSWORD=(接続パスワード)
DB_SERVER=(接続先IPアドレスかホスト名)
DB_DATABASE=(使用DB名)
以降、解説です。
(1)npmのmssqlを使用する
//@deno-types="npm:@types/mssql" //(1)
import mssql from "npm:mssql@9.1.1"; //(1)
denoのライブラリにhttps://deno.land/x/mssql@v.0.0.2
がありますが、
これはどうやら Implementation in progress ・・・実装中とのことです。(2023/8/11時点情報)
代わりにnpmのmssql@9.1.1
を使用します。
@typesパッケージをimport
の1行上で//@deno-types=
のコメントで指定することにより、型チェックが効くようになります。
VSCode + Deno の環境なら、コード補完もされる様になります。
(2)接続先を.envで指定する
import "https://deno.land/std@0.193.0/dotenv/load.ts"; //(2)
//・・・
//(2)
const conPool = new mssql.ConnectionPool({
user: Deno.env.get("DB_USER"),
password: Deno.env.get("DB_PASSWORD"),
server: Deno.env.get("DB_SERVER") as string,
database: Deno.env.get("DB_DATABASE"),
Denoのdotenv
による.env
や環境変数の読込方法はいろいろな情報がある様子ですが、
標準ライブラリを使用するとこのような感じです。
Nodeとは異なり、標準ライブラリでできるのが良いですね。
(3)一部のオプション指定が必要
//(3)
options: {
//denoの場合、npmのtlsが使えないため false にする必要あり。
encrypt: false,
//これをfalseにしないと、問合せ結果の日時がUTCになってしまう。
useUTC: false,
},
https://github.com/denoland/deno/issues/17842 によると、
Denoはnpmのtlsを使用できないとのことです。(2023/8/11時点)
そのため、tlsを用いた暗号化ができません。
ローカルネットワーク上の SQL Server に接続することが目的のため、本件では暗号化不要です。
そのため、encrypt: false
で暗号化を無効にします。
また、DB側が返す日時がUTCやGMTでない場合(例えば日本標準時の場合)、useUTC: false
が必要です。
これをしないと、DB側が返した日時をUTCと認識してしまい、日本だと9時間のズレが発生します。
(例 DBからの返し:日本標準時2023/8/11 20:00:00 → Denoの認識:UTCで2023/8/11 20:00:00、日本標準時で2023/8/12 5:00:00)
(4)using構文で確実に切断する
import {
Disposable,
using,
} from "https://deno.land/x/disposable@v1.1.1/mod.ts"; //(4)
//・・・
//(4)
await using(
(await conPool.connect()) as mssql.ConnectionPool & Disposable,
async (con) => {
con.dispose = con.close;
const dbReq = con.request();
const query = `SELECT GETDATE() AS DBDATE;`;
const rs = await dbReq.query(query);
console.log(rs.recordset[0]["DBDATE"].constructor.name); //→ Date
console.log(rs.recordset[0]["DBDATE"]); //ex→ Fri Aug 11 2023 22:42:27 GMT+0900 (日本標準時)
}
);
Denoはどうやら接続後にちゃんと切断しなくても、接続用変数が何処からも参照できなくなれば
ガベージコレクタか何かが自動的に切断してくれる様子です。
しかし、環境次第ですが私の検証では自動切断まで約20秒かかっていました。
c#などにあるusing構文で、不要になったら確実に切断させたいです。
using構文はTypeScript5.2で予告されていますが、現時点(2023/8/11時点)ではまだないので、
サードパーティ製ライブラリ https://deno.land/x/disposable@v1.1.1/mod.ts で実現します。
要点は以下2点です。
・(await conPool.connect()) as mssql.ConnectionPool & Disposable,
のダウンキャストにより、
using構文対象の変数は本来の型mssql.ConnectionPool
であることと
using構文対象の条件である型Disposable
であることを示す。
↓
・しかし、実際は型Disposable
であることを満たすメソッドdispose
が実装されていない。
con.dispose = con.close;
によりdispose
を実装する。
これにより、await using
を抜けた時点で接続が切断されます。
以上です。