🦕

Deno 1.37.0 で Nodemailer が使えるようになったので試してみた

2023/09/25に公開

目次

はじめに

アップデイティットの毛利です。

みなさん、Deno 使ってますか?

先日、Bun が晴れて V 1.0 を迎えたり、 Nue JS がリリースされたりと、Alt Node.js の選択肢がさらに広がってきています。
そんな中、Deno も頑張っていますが、普及の波に乗るにはもうひと踏ん張りな印象があります。
State of JS 2022 の JAVASCRIPT RUNTIMES の項を参照)

さて、そんな Deno も 先日 V 1.37.0 を迎えました。
このバージョンでは、 Node.js 依存ライブラリとの互換性が更に向上しています。

具体的な事例として、 Nodemailer が動きます。すばらしい。
Deno 上でまともに動く SMTPの通信ライブラリの選択肢がようやく現れたのです。

今回は、とても簡単にですが、情報通り動作するか手を動かしてみたいと思います。

動機、環境など

この記事のゴール

  • Deno V 1.37.0 をインストールする
  • Deno 上で Nodemailer を呼び出してメールを送信する

動機

Nodemailer が動けば、 Deno を基盤にメール送信機能を内包したものを作れる!
特に、前回の

にて、言葉を濁していたメール周りの機能を拡充することができる!!

想定読者

  • JavaScript / TypeScript が少しでも読める人。
  • 自身のマシン/サーバー にプログラムをインストールできる人。

環境

  • Windows 11 on NotePC
    • 今回はローカルPC環境から実行します。
    • CLI は PowerShell を使います(CMDでも何でも良いです)
    • WSLは今回使いません
  • Deno
    • V 1.37.0 をいれます
  • メールサーバー
    • 所有していない場合、検証目的で Gmail の SMTPサーバー を使用したり、AWS SES などを使って新規発行したりなど。

作業

1. Deno をインストールする

私は、 Windows マシンに入れるアプリケーションを、極力 Chocolatey 経由で入れています。
理由は単純で、数年に1回ローカル開発機を入れ替えるときの時間的なコストを極力削減したいためです。

PowerShell
cinst -y deno
# 既に入っている場合は choco upgrade -y deno

chocolatey を使えない場合は、Deno公式の方法で入れてください。

2. deno init する

作業ディレクトリを作成したら、deno init を行います。

PowerShell
mkdir deno_mail_test
cd deno_mail_test
deno init

いろいろ出てきますが、main.ts と deno.jsonc 以外は基本触りません。

先に、deno.jsonc にコマンドを追加します。

deno.jsonc
{
  "lock": false,
  "tasks": {
    "dev": "deno run --watch main.ts",
    "prod": "deno run --allow-all main.ts"
  }
}

--watch 引数のない実行コマンドを用意します。
(deno task dev でメール送信コードを触ると、保存の度に再実行されるのでメールが想定外のタイミングで飛びます笑)

また、今回はテストなので「--allow-all」で全許可します。
本当は適切に制御しなければダメです。

3. メール送信プログラムの作成

まずファイルを用意します。

PowerShell
New-Item -ItemType File mail.ts

VS Code や Vim なりで中身を書きます。

mail.ts
import * as nodemailer from "npm:nodemailer@6.9.5";

const MAIL_SETTINGS = {
  smtp_host: "*************",
  smtp_port: 465,
  smtp_auth_user: "*************@*************",
  smtp_auth_pass: "*************",
  smtp_ssl: `SSL`,
  smtp_from: `*********@*************`,
};

function buildMessage(to: string, subject: string, text: string) {
  return {
    from: MAIL_SETTINGS.smtp_from,
    to,
    subject,
    text,
  };
}

const transporter = nodemailer.createTransport({
  pool: false,
  host: MAIL_SETTINGS.smtp_host,
  port: MAIL_SETTINGS.smtp_port,
  secure: MAIL_SETTINGS.smtp_ssl === `SSL`,
  auth: {
    user: MAIL_SETTINGS.smtp_auth_user,
    pass: MAIL_SETTINGS.smtp_auth_pass,
  },
});

export const testMail = () => {
  transporter.sendMail(
    buildMessage(
      /* to address */ "*****************@*************",
      /* Subject    */ "test from nodemailer",
      /* Body       */ "hello, this is test mail from nodemailer"
    )
  );
};

見ての通り、最低限の設定のみ、かつ実値はほぼ全て濁していますので、適宜、自分の環境に併せてください。
このコードの testMail() を叩くことでメールが定型文、かつ固定の指定先に飛ぶので、main.ts から呼び出します。

main.ts
import { testMail } from "./mail.ts";

if (import.meta.main) {
  testMail();
}

あとは、実行するだけです。

4. 実行

あとは、最初に追記した prod コマンドを実行すればOKです。

PowerShell
deno task prod

これでメールが送信されました。

5. 確認

私の試験環境では、手元のThunderbirdでログインしているメールアドレスに送信したので、

このように結果が確認できました。
お疲れ様でした。

[opt.] 古い deno で動かしたらどうなるの?

試しに、Docker Compose を使って、古いDenoの実行環境を呼び出してみます。
(Docker Compose 周りのセットアップは割愛します。)

PowerShell
New-Item -ItemType File docker-compose.yml
New-Item -ItemType File deno.Dockerfile
docker-compose.yml
version: "3"

services:
  deno:
    container_name: deno
    hostname: deno
    build:
      context: .
      dockerfile: deno.Dockerfile
deno.Dockerfile
FROM denoland/deno:distroless-1.29.1

COPY mail.ts main.ts ./

CMD ["run", "--allow-all", "main.ts"]

前回の記事で使用していたバージョンが V 1.29.1 だったため、こちらを呼び出します。
以下、実行結果です。

errorlog
deno  | error: Uncaught Error: Error initiating TLS - tlssock._start is not a function
deno  |     at file:///deno-dir/npm/registry.npmjs.org/nodemailer/6.9.5/lib/smtp-connection/index.js:1392:31
deno  |     at SMTPConnection._upgradeConnection (file:///deno-dir/npm/registry.npmjs.org/nodemailer/6.9.5/lib/smtp-connection/index.js:921:20)
deno  |     at SMTPConnection._actionSTARTTLS (file:///deno-dir/npm/registry.npmjs.org/nodemailer/6.9.5/lib/smtp-connection/index.js:1390:14)
deno  |     at SMTPConnection._processResponse (file:///deno-dir/npm/registry.npmjs.org/nodemailer/6.9.5/lib/smtp-connection/index.js:969:20)
deno  |     at SMTPConnection._onData (file:///deno-dir/npm/registry.npmjs.org/nodemailer/6.9.5/lib/smtp-connection/index.js:755:14)
deno  |     at Socket.SMTPConnection._onSocketData (file:///deno-dir/npm/registry.npmjs.org/nodemailer/6.9.5/lib/smtp-connection/index.js:193:44)
deno  |     at Socket.emit (https://deno.land/std@0.168.0/node/_events.mjs:379:28)
deno  |     at _t (https://deno.land/std@0.168.0/node/_stream.mjs:10:32972)
deno  |     at ir (https://deno.land/std@0.168.0/node/_stream.mjs:10:32699)
deno  |     at Socket.w.push (https://deno.land/std@0.168.0/node/_stream.mjs:10:31925)
deno exited with code 1

こういうエラーとなります。
最新の Deno ではこのあたりの互換性が改善された、ということでしょう。

所感

そもそも Deno でSMTP経由したメールを飛ばしたいというニーズが少なかったと思われるので、今回のアップデートが良い意味で Deno を取り巻く状況を改善するとはあまり思えませんが、個人的には非常に嬉しいアップデートでした。

実は、Deno にもサードパーティーライブラリとして SMTPライブラリがあるのですが、様々な要因から、開発があまりアクティブではなく、動かない機能も多いようです(例えば STARTTLS で送信できないとか)。
今回、 Nodemailer が動いてしまったということは、同時にNode.jsベースの歴史ある強力なプロダクトが使えるようになったということで、ライブラリ開発においては、Deno 専用でライブラリを作るメリットが少なくなる懸念があります。
(最近は、そういうのも見越して、使い回しやすくWASMで作ればいいじゃんとか、ありますね。)

このあたりは、ちょっとなんとも言えない気持ちがありますが、ともあれ、引き続き Deno を使って応援していきたいと思います。アイコン可愛いので。

おわりに

弊社では、プロジェクト運営にも活用できるドキュメント開発システム「crossnote」や、定期レポート作成の自動化にも活用できるクラウドサービス「EDITROOM」といったサービスを、B2B 向けに展開しています。
特に、「ドキュメント開発」と呼称されるような、共同作業によって1つの文書を作成・レビューするような環境を提供することに関しては、実績と信頼があると自負しております。

これらプロダクトにご興味がありましたら、Deno(+Fresh)で動いている 弊社お問い合わせ窓口を経由して、お気軽にお問い合わせください。
トライアル環境を用意するなど、すぐにお試しいただけるよう対応させていただきます。

Discussion