LoginSignup
1
0

DenoでSQL Serverに接続して問合せするだけ

Posted at

この記事は何か

denoでローカルネットワーク上のSQL Serverに接続して問合せする、
ただそれだけのために結構つまづきました。
あまり需要が無いかもしれませんが、成功した実装を紹介します。

結論

以下のコードで接続と問い合わせができました。
コメント内の(1)などは、後述の解説と対応します。

main.ts
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 (日本標準時)
  }
);
.env
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を抜けた時点で接続が切断されます。

以上です。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0