20191129のNode.jsに関する記事は6件です。

libsassのalpine向けバイナリーの提供状況

node-sassにバンドルされるlibsassはalpine向けにもバイナリーを配布している。が条件あり。

  • 利用してるNode.jsのversionによって、alpine向けに配布をしてるバージョンの範囲が変わる
  • 開発版サポートが先に提供中止になる流れ
    • 次回or 次々回リリースでは v11向けのalpineバイナリー提供がなくなるっぽい

Node v13: 最新版 v4.13.0 から配布

https://github.com/sass/node-sass/releases/tag/v4.13.0

Node v12: v4.12.0から配布

https://github.com/sass/node-sass/releases/tag/v4.12.0

Node v11: v4.10.0から配布

https://github.com/sass/node-sass/releases/tag/v4.10.0

  • おそらくv4.13.0が最後の配布versionに

Node v10: v4.9.0から配布

https://github.com/sass/node-sass/releases/tag/v4.9.0

Node v9: v4.6.0からv4.10.0まで配布

https://github.com/sass/node-sass/releases/tag/v4.6.0

  • v4.11.0 以降は提供されず
  • 次世代の開発バージョンに配布開始したversionが最後の配布versionに

v8: v4.5.3~

備考

alpineを使う利点

  • イメージサイズが公式イメージと比べて 1/10以下
    • 10.15.1だと、897MB=> 70.7MB
  • インストール所要時間の削減

サポートしてないnode.js alpineイメージへnode-sassをインストールしたい

gypなどを入れればok

apk add --no-cache --virtual .gyp python make g++ \
    && apk --no-cache add avahi-dev \
    && yarn global add mdns \
    && apk del .gyp
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

word2vecの勉強で「ナダルリバースエボリューション」が再現できるのではないかと思いついたのでやってみた

この記事はプロトアウトスタジオ アドベントカレンダー2019 3日目です
プロトアウトスタジオ一期生のおのです!

はじめに

以前投稿した記事の 「しらける」を「ホワイトキック」に自動変換したかったストーリーの改善を考えたときにword2vecを使用しようと思ってました。
word2vecやらなきゃなぁって考えてたところ、別の話で「ナダルリバースエボリューション」(懐かしい)の話が出て…

あ、これ、再現できるかも。

と思ってしまったのでやるしかないのかなこれは。
ということでnodejsを使って勉強がてらやってみました。
アドベントカレンダーのネタにもなるし…

ナダルリバースエボリューションって?

https://matome.naver.jp/odai/2145175669028461001
https://matome.naver.jp/odai/2145762091988093801

まとめられてますねぇ
例えば
- 「えんぴつ」→「シャーペン」
- 「松本人志」→「ボブサップ」

word2vec的に考えると
- 「えんぴつ」→「シャーペン」は、なんか近代的に?機能が追加された感じですかね
- 「松本人志」→「ボブサップ」は、松本人志の特徴の筋肉部分をもっと増やした感じで、他の部分はほぼほぼ考えられてない感じ

これを整理する前は、進化前のwordのネガティブ部分をポジティブに変えればいいかなと考えてたがそうじゃなく、進化前の特徴をより強調させたり、増やしたり、ベテランにしたり、えらくしたりっぽいですね

これをword2vecを利用して再現できるのか

環境

実装

モデルデータの用意

データを作るところからやるのもできますが、自分は以下のサイトからお借りいたしました。
http://www.cl.ecei.tohoku.ac.jp/~m-suzuki/jawiki_vector/

モデル読み込みテスト

npm i word2vec

でライブラリをインストール
上記のサイトからダウンロードしたモデルを同じディレクトリに入れ、

const w2v = require("word2vec");

console.log("モデル読み込み開始");
w2v.loadModel("./entity_vector/entity_vector.model.txt", (error, model) => {
  console.log("モデル読み込み完了");
  console.log(model);
});

どうでしょう
自分はここでエラーが起きました。
エラーをガッツリ載せますが

<--- Last few GCs --->

[49239:0x10284f000]    16619 ms: Mark-sweep 1991.1 (2087.8) -> 1981.1 (2090.2) MB, 135.4 / 0.0 ms  (average mu = 0.163, current mu = 0.135) allocation failure scavenge might not succeed
[49239:0x10284f000]    16862 ms: Mark-sweep 1993.9 (2090.2) -> 1984.3 (2091.8) MB, 220.8 / 0.0 ms  (average mu = 0.125, current mu = 0.092) allocation failure scavenge might not succeed


<--- JS stacktrace --->

==== JS stack trace =========================================

    0: ExitFrame [pc: 0x100e0b7a6]
Security context: 0x2f707e39a2f1 <JSObject>
    1: ondata [0x2f7098fad841] [readline.js:~169] [pc=0xcbb89e0fa1f](this=0x2f7098fad8f9 <ReadStream map = 0x2f70bfdcdc09>,0x2f70d8f5fa29 <Uint8Array map = 0x2f70bfda56b9>)
    2: emit [0x2f707e3d35d9] [events.js:~149] [pc=0xcbb89e030bd](this=0x2f7098fad8f9 <ReadStream map = 0x2f70bfdcdc09>,0x2f707e3806f9 <String[#4]: data>)
    3: arguments adaptor frame: 2->1...

FATAL ERROR: Ineffective mark-compacts near heap limit Allocation failed - JavaScript heap out of memory
 1: 0x1000760ab node::Abort() [/Users/takayuki/.nvm/versions/node/v12.3.1/bin/node]
 2: 0x1000767ec node::errors::TryCatchScope::~TryCatchScope() [/Users/takayuki/.nvm/versions/node/v12.3.1/bin/node]
 3: 0x100161327 v8::Utils::ReportOOMFailure(v8::internal::Isolate*, char const*, bool) [/Users/takayuki/.nvm/versions/node/v12.3.1/bin/node]
 4: 0x1001612bc v8::internal::V8::FatalProcessOutOfMemory(v8::internal::Isolate*, char const*, bool) [/Users/takayuki/.nvm/versions/node/v12.3.1/bin/node]
 5: 0x10053fc25 v8::internal::Heap::FatalProcessOutOfMemory(char const*) [/Users/takayuki/.nvm/versions/node/v12.3.1/bin/node]
 6: 0x100540d13 v8::internal::Heap::CheckIneffectiveMarkCompact(unsigned long, double) [/Users/takayuki/.nvm/versions/node/v12.3.1/bin/node]
 7: 0x10053e713 v8::internal::Heap::PerformGarbageCollection(v8::internal::GarbageCollector, v8::GCCallbackFlags) [/Users/takayuki/.nvm/versions/node/v12.3.1/bin/node]
 8: 0x10053c3cf v8::internal::Heap::CollectGarbage(v8::internal::AllocationSpace, v8::internal::GarbageCollectionReason, v8::GCCallbackFlags) [/Users/takayuki/.nvm/versions/node/v12.3.1/bin/node]
 9: 0x100546eb5 v8::internal::Heap::AllocateRawWithLightRetry(int, v8::internal::AllocationType, v8::internal::AllocationAlignment) [/Users/takayuki/.nvm/versions/node/v12.3.1/bin/node]
10: 0x100546f2f v8::internal::Heap::AllocateRawWithRetryOrFail(int, v8::internal::AllocationType, v8::internal::AllocationAlignment) [/Users/takayuki/.nvm/versions/node/v12.3.1/bin/node]
11: 0x100518ac5 v8::internal::Factory::NewRawTwoByteString(int, v8::internal::PretenureFlag) [/Users/takayuki/.nvm/versions/node/v12.3.1/bin/node]
12: 0x1005183df v8::internal::Factory::NewStringFromUtf8(v8::internal::Vector<char const>, v8::internal::PretenureFlag) [/Users/takayuki/.nvm/versions/node/v12.3.1/bin/node]
13: 0x1001841c5 v8::String::NewFromUtf8(v8::Isolate*, char const*, v8::NewStringType, int) [/Users/takayuki/.nvm/versions/node/v12.3.1/bin/node]
14: 0x1000f897d node::(anonymous namespace)::MakeString(v8::Isolate*, char const*, unsigned long, node::encoding) [/Users/takayuki/.nvm/versions/node/v12.3.1/bin/node]
15: 0x1000f88f3 node::StringDecoder::DecodeData(v8::Isolate*, char const*, unsigned long*) [/Users/takayuki/.nvm/versions/node/v12.3.1/bin/node]
16: 0x1000f910d node::(anonymous namespace)::DecodeData(v8::FunctionCallbackInfo<v8::Value> const&) [/Users/takayuki/.nvm/versions/node/v12.3.1/bin/node]
17: 0x100e0b7a6 Builtins_CallApiCallback [/Users/takayuki/.nvm/versions/node/v12.3.1/bin/node]
18: 0xcbb89e0fa1f 
19: 0xcbb89e030bd 
20: 0x100e03c3c Builtins_ArgumentsAdaptorTrampoline [/Users/takayuki/.nvm/versions/node/v12.3.1/bin/node]
21: 0xcbb89e0cbda 
[1]    49239 abort      node app.js

実行時にメモリが足りないそうです。
https://qiita.com/shohei_ot/items/5fbc7641fa2ebae9c911

なので実行を

node --max_old_space_size=8192 app.js

にしてます。4096でも足りなかったでした…
おすすめはこの実行コマンドをpackage.jsonscriptsに入れておくことをおすすめします。
https://qiita.com/minase_tetsuya/items/986feac7150ed74e13d8
しかも自分はmacbookproなのですが、1分弱は読み込みにかかりますね。

進化させよう

word2vecの機能を見てみよう

技術はなんとなく使えそうになりました。
ここを参考にnode-word2vecでできることをまとめてみます。

loadModel後に取得できるmodelデータのメソッドには
- .similarity( word1, word2 ) 単語と単語の近さを取得
- .mostSimilar( phrase[, number] ) 引数の単語に近い単語を取得
- model.analogy( 'woman', [ 'man', 'king' ], 10 ); word2vecらしいやつ。下で説明
- .getVector( word ) vecterデータを取得
- .getNearestWord( vec ) vecterデータから単語取得

vecterデータのプロパティとメソッドは
- .word 単語がはいってる
- .values vecterのデータが配列で
- .add( wordVector ) vecterの足し算 あとでも記述するが、単語の意味の足し算にはならなそう
- .subtract( wordVector ) vecterの引き算

analogyですが、よく「王様 - 男 + 女性 = 女王」という例がありますが、
こちらでは、「男性における王様は女性における〇〇」として表現してます。

進化前の単語をユーザー入力に

実行のたびにモデルを読み込むので時間がかかる!
ので、実行後にユーザー入力に受け取ってみます。

const rl = require("readline").createInterface(process.stdin, process.stdout);
rl.on("line", (str) => {
  console.log("get:" + str);
});

これをもとに

const w2v = require("word2vec");

console.log("モデル読み込み開始");
w2v.loadModel("./entity_vector/entity_vector.model.txt", (error, model) => {
  console.log("モデル読み込み完了", model);
  console.log("進化させたいものを入力してください");

  const rl = require("readline").createInterface(process.stdin, process.stdout);
  rl.on("line", function(str) {
    if (model) {
      try {
        console.log(model.mostSimilar(str));
      } catch (e) {
        console.log(e);
      }
    }
  });
});

try catchしている理由は、modelデータにない単語を入力してしまうとエラーで落ちてしまうからです。
そしたらまた1分ロード読み込みまちは嫌なので

これを基礎に、以下に記載していくコードは基本このtry内のことです

鉛筆におけるシャーペンを進化前における進化後

strはユーザーの入力値で、進化前の単語ということです

 console.log(model.analogy(str, ["鉛筆", "シャーペン"], 3));

結果
Image from Gyazo

松本人志が難しいのはなんとなくわかりますが、人以外ばかり
卓球はなんとなく…セパタクローとかナダルが言ったらたしかに面白いかも

弱いにおける強いを進化前における進化後

console.log(model.analogy(str, ["弱い", "強い"], 3));

結果

Image from Gyazo

松本人志も人にはなったし、卓球もブレずにスポーツだがセパタクローを超えない。
Tシャツはウェディングドレスってきたら流石に強すぎる。が、面白いかも
弱いではなくて、なにか進化前のものの特徴的な部分をより強くだと考えてたけど、これはこれでいいのかも…

「すごい」のvecterを進化前に足して進化後を作る

let evoVector = model.getVector("すごい");
let vec = model.getVector(str).add(evoVector);
console.log(model.getNearestWord(vec));

結果
Image from Gyazo

これは失敗です。
vecterの足し算は単語の足し算とは違うのかなぁ多分足し算しちゃって1を超えちゃったりしたのかも

すごい+進化前で進化後を作る

console.log(model.mostSimilar("すごい" + str));

結果
Image from Gyazo

これは自分がアホでした。
「すごい松本人志」なんて単語はあるはずない。

ナダル(人)が思う進化前と進化後のvectorの差で作る

一番最初に試した鉛筆シャーペン。この2つの単語がもっとたくさんあって、そこから選ぶとか、そもそもそれらの差を求めれば進化とはに迫る検証ができるのかと思いました。

まとめサイトからナダル発言の進化前と進化後を集めてvectorの差を取るぞ!
このvectorデータこそが人が進化するのに必要なデータかもしれない(錯乱)

では、スプレットシートにまとめてみました。

Image from Gyazo

(映っちゃいけない人が…大丈夫でしょう)

ということでやってみたんですが、
Image from Gyazo

うーん人物を進化させることが多いらしく、単語として登録されていないものばかりであまり有用ではありませんでした。
残念。
たとえこれで進化のvectorデータが取れたとしても、それを進化前の単語に単純に足すのは結局良くないんじゃないのかなぁ…うーむ

まとめ

すごく勉強になったが、簡単に進化はできなかった。
進化で思いついたけど、ヒトカゲリザードンとかアグモングレイモンとかも出せたらいいね
ちなみにナダルの進化は圧倒的に見た目で決めてますよね。
そりゃこんな結果になるね

次は tseigoさん です。paiza.cloudで何かしてくれるそうです。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Expressフレームワークのインストールと簡単な使い方

Expressとは

Expressは、Node.jsのフレームワークで、以下の特徴があります。
・セキュリティ性能が高い。
・無数のHTTPに関連するメソッドとミドルウェアを使用できる。
・セキュリティ性能の高いAPIがすばやく簡単に作成できる。
・Node.jsの機能を分かりづらくすることがない。
Express公式サイト

英語翻訳なので分かりづらいと思いますが、Node.jsのアプリが「簡単に、素早く、安全に」作れるという認識で大丈夫だと思います。

Expressでサーバーを起動する。

公式サイトを見れば、わかるのですが、以下のようにインストールをします。
まずはアプリを保存するディレクトリを作成します。

console
//myappというディレクトリを作成
$ mkdir myapp
//myappディレクトリに移動
$ cd myapp

続いて、package.jsonファイルを作成を作成します。

console
//package.jsonファイルを作成
$ npm init

質問にはすべてenterキーを押して大丈夫です。

続いて以下のようにexpressをインストールします。
--saveを使うことで、package.jsonにexpressのバージョン情報などが書き込まれます。

console
//expressのインストール
$ npm install express --save

これでexpressのインストールが完了しました。
それでは、以下のようにexpressの基準となるapp.jsに移動します。

console
$ cd app.js

続いて、app.jsでサーバーを起動するための記述をしていきます。

app.js
//expressモジュールを使えるように設定
const express = require('express')
//expressモジュールを利用しアプリケーションオブジェクトappを作成
const app = express()

// HTTPgetメソッドでアクセスしたら、'Hello World!'と表示される設定。
app.get('/', (req, res) => {
  res.send('Hello World!')
})

//サーバーを起動したら、リクエストを8000番ポートで待ち受ける設定。
app.listen(8000, () => console.log('Example app listening on port 8000!'))

それでは以下のようにサーバーを起動します。

console
$ node app.js
> Example app listening on port 8000!

サーバーを起動したら、http://localhost:8000/ にアクセスしてみてください。

Expressジェネレーターを使う。

以上のようにサーバーを起動しても良いのですが、expressには、ジェネレーターというひな形作成モジュールがあります。ここで言う、ひな形とは、アプリ作成に必要な機能がセットになったものを指します。

それでは、ジェネレーターを使っていきます。以下のように、express-generatorをグローバルインストールします。

console
$ npm install express-generator -g

インストールが完了したら、下記コマンドでアプリケーションのひな形を作成します。
--view=pugで、views内のファイルにpugというテンプレートエンジンを使う設定になります。

console
$ express --view=pug myapp2

すると、 以下のようにコンソールに表示されます。
これはひな形が自動的に作られたことを意味します。

console
   create : myapp
   create : myapp/package.json
   create : myapp/app.js
   create : myapp/public
   create : myapp/public/javascripts
   create : myapp/public/images
   create : myapp/routes
   create : myapp/routes/index.js
   create : myapp/routes/users.js
   create : myapp/public/stylesheets
   create : myapp/public/stylesheets/style.css
   create : myapp/views
   create : myapp/views/index.pug
   create : myapp/views/layout.pug
   create : myapp/views/error.pug
   create : myapp/bin
   create : myapp/bin/www

続いて、myappに移動し、必要な依存モジュールをインストールします。

console
$ cd myapp
$ npm install

これでひな形が完成しました。
サーバーを起動していきましょう。

console
$ DEBUG=myapp:* PORT=8000 npm start

このコマンドは、
DEBUGという環境変数にmyapp:*という値を設定し、myapp:とついたログが表示されるようにしています。
PORTという環境変数に8000という値を設定し、8000番ポートでサーバーが待ち受けるようにしています。
npm startは、サーバーを起動するという意味になります。
サーバーを起動したら、http://localhost:8000/ にアクセスしてみてください。
Express Welcome to Expressと表示されたら成功です。

続いて、自動生成されたひな形を見ていきます。

.
├── app.js
├── bin
   └── www
├── package.json
├── public
   ├── images
   ├── javascripts
   └── stylesheets
       └── style.css
├── routes
   ├── index.js
   └── users.js
└── views
    ├── error.pug
    ├── index.pug
    └── layout.pug

app.jsはサーバーなどを起動する基本ファイルです。
bin/www は、サーバー起動などの情報が書き込まれています。
publicディレクトリには、画像や、クライアントJavaScript、CSSなどの静的ファイルが格納されています。
routesディレクトリには、ルーティングを行うファイルが格納されます。
viewsディレクトリには、pugテンプレートファイルが格納されています。主にhtmlのような画面上での役割を果たします。

次に簡単なルーティングを見ていきます。
ルーティングとは、クライアントの要求に応じて処理を振り分けることです。
例えば、/indexにGETメソッドで要求があった際には以下のようにします。

app.get('/index', (req, res) => {
  res.write('Hello World!');
});

以上は以下のような構造になっています。

app.METHOD(PATH, HANDLER)

app は、expressのアプリケーションオブジェクトのインスタンスです。
アプリケーションオブジェクトのインスタンスは、use関数を使うことで、ミドルウェアやルーティングを使うことができます。
METHOD は、HTTP要求メソッド です。
PATH は、サーバー上のパスです。
HANDLER は、ルートが一致したときに実行される関数です。

最後に、静的ファイルの取り扱い方についてです。
image、CSS、JavaScriptなどの静的ファイルを利用するには、Expressに標準実装されているexpress.staticミドルウェア関数を使用します。

app.use(express.static('public'));

これで、/public以下のファイルをブラウザから見ることができるようになります。

http://localhost:8000/css/style.css にアクセスしてみてください。
style.cssの中身が表示されるはずです。

おまけ ミドルウェアの使い方

ミドルウェアは、標準装備かサードパーティ(helmetなど) 製のものがあり
、Expressの機能を拡張してくれるモジュールのことを指します。
helmetだったら、以下のように使います。

var helmet = require('helmet');
app.use(helmet());

参考文献:Express公式

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

GAE に JSON 取得の Web サーバーを構築して SendGrid からメールする

Google Cloud Platform(以下、GCP)環境で、"Web サーバーへのリクエストからメール送信サービスを利用する"ということをしてみたかったので、色々確認してみました。

元々、AWS ばかり触っていて、AWS では AWS Lambda と Amazon SES 利用すれば同様のことができるというのはわかっていたのですが、今回は GCP 環境で作業するという縛りがあったので、「GCP でのメール送信サービスにあたるものは何か?」から「どうやって実装するのか?」を調べました。

調べると、Google App Engine(以下、GAE)と SendGrid を利用する方法が、Google のドキュメント で推されていたので試してみました。

※ 単純にやると、内容が Google ドキュメントとほぼ同じになりそうだったので、ドキュメントでは"フォームデータ"の POST を例にしていたのを、当記事では"JSON データ"での POST にしています。更に、ドキュメントでは"決まった内容のメール"しか送信できないので、JSON の内容に合わせて可変になるように変更しています。

概要

上記したように、"Web サーバーへのリクエストからメール送信サービスを利用する"ものを実装します。それを今回の GCP を関連のサービスを利用するという目的に当てはめ、、、

"GAE 上に Web サーバーを構築し、JSON データの POST リクエストを処理し、SendGrid ライブラリを利用して、メールを送信する"

Screen Shot 2019-11-29 at 14.00.21.png

という形で構築します。

Google App Engine とは

皆さんご存知かもしれませんが、知らない方もいるかもしれないので軽く説明しておきます。

今回利用する GAE は、Google のインフラ上でアプリケーションを作成し、実行できるようにする PaaS あたるものです。PaaS の特長の通り、プラットフォームを特にメンテナンスする必要なくサービスを構築できるという点で優れています。

ぶっちゃけ、今回目的としている内容は、Google Cloud Functions を使えば実現可能です。ただ特に HTTP 以外のイベント駆動も考えていなかったので、単純に GAE を使ってみました。(IaaS 系の Google Compute Engine 実現可能ですね!)

ちなみに、GAE を AWS サービスに当てはめると、AWS Elastic Beanstalk になるかと思います。Cloud Functions が AWS Lambda、Google Compute Engine が Amazon EC2 ですかね。(全く同じではないので対比するものでないと思いますが…)

SendGrid とは

GCP 内に存在するサービスでなく、サードパーティのメール送信サービスです。毎月 12,000 件のメールを GCP ユーザーにトライアルとして無料で提供してくれています。また、SendGrid を使用すると、メールの到達率が向上し、アプリから送信されたメールの状況をより明確に把握することができるようです。

Screen Shot 2019-11-29 at 11.40.47.png

上図のような SendGrid インターフェースまたは API を介して、開封数、クリック数、登録解除、スパムレポートなどに関する統計情報を確認することが可能です。

今回は "SendGrid" を選択しましたが、Google のドキュメントでは "Mailgun"、"Mailjet"も候補に挙がっていました。

サードパーティのサービスなので、GCP 以外の環境からでも SendGrid は利用できます。

実装

基本的には、チュートリアル「インスタンスからのメール送信」に従って実装しています。上記したように少し内容を変え、2019/11/27 時点で動くものとして、手順を記載していきます。

  1. GCP プロジェクトを作成
  2. Marketplace より"SendGrid Email API"の無料プランを開始
  3. SendGrid から API Key を取得
  4. GAE へ Web サーバーをデプロイ
  5. JSON データを POST 送信

1. GCP プロジェクトを作成

GCP にアクセスし、プロジェクトを作成します。後に GAE を使用するのと、SendGrid Email API を有効化するのに必要になるので、プロジェクトに対して課金設定をしておきます。

2. Marketplace より "SendGrid Email API" の無料プランを開始

Marketplace タグで "SendGrid"と検索し、"SendGrid Email API" のページを開きます。
"無料プランを開始"より SendGrid Email API を有効化します。

私はここで若干ハマったので注意点を下記しておきます。。。

まずプロジェクトの課金設定が行われていないと"無料プランを開始"ボタンが、グレーアウトされた状態になります。
更に課金設定を行っていても、"SendGrid Email API" を有効化するアカウントに課金設定の操作が可能な権限がないと同じく"無料プランを開始"ボタンがグレーアウトされた状態になります。
Screen Shot 2019-11-29 at 11.46.15.png

GCP での課金をグループで管理していて、自身が課金設定の管理者でない場合、上記のような状態になり得るので注意が必要です。課金設定の管理者に、プロジェクトの課金有効化と、Marketplace での "Sendgrid Email API" の有効化をセットでお願いしましょう。

3. Sendgrid から API Key を取得

Marketplace の Sendgrid Email API ページで、"無料プランを開始"をクリックした後、Subscribe 項目で利用プランの選択を、Activate 項目で SendGrid アカウントの登録を行います。そうするといつもの Google 認可画面が出てくるので、許可をクリックします。
以上で、SendGrid Email API の有効化が完了です。完了後、切り替わる画面上に "SendGrid ウェブサイトで API キーを管理する"ボタンがあるのでクリックすると、SendGrid のウェブサイトに遷移します。

SendGrid ウェブサイトの "Settings" から、"API Keys"タブを開き、"Create API Key" ボタンをクリックすると API Key が発行されるので文字列をコピーしておきます。

4. GAE へ Web サーバーをデプロイ

App Engine を開きます。プロジェクトの課金が有効になっていないと操作できないので、課金設定を有効にしておく必要があります。

今回は Nodejs のバージョン v10.10 で作成していきます。
GAE デプロイのため、下記ファイルを作成します。

  • app.flexible.yaml(GAE を構成するファイル)
  • app.js(GAE 上で動く処理ファイル)

加えて、Package.json の修正も必要になります。

まず、app.flexible.yaml からです。

app.flexible.yaml
runtime: nodejs10
env_variables:
  SENDGRID_API_KEY: <your-sendgrid-api-key>
  SENDGRID_SENDER: <your-sendgrid-sender>

"env_variables" には、app.js が処理で利用する環境変数を記載します。
"SENDGRID_API_KEY" は、SendGrid ウェブサイトで取得した API Key を、"SENDGRID_SENDER" は、送り元となるメールアドレスを記載します。

app.js ファイルに、GAE での処理を記載していきます。

app.js
'use strict';

const express = require('express');
const bodyParser = require('body-parser');

const { SENDGRID_API_KEY } = process.env;
const { SENDGRID_SENDER } = process.env;
const Sendgrid = require('@sendgrid/client');

Sendgrid.setApiKey(SENDGRID_API_KEY);

const app = express();

app.use(bodyParser.json());

app.post('/hello', async (req, res, next) => {
   console.log(req.body);
   const request = {
      method: 'POST',
      url: '/v3/mail/send',
      body: {
         personalizations: [
            {
               to: [{ email: req.body.email }],
               subject: req.body.sub,
            },
         ],
         from: { email: SENDGRID_SENDER },
         content: [
            {
               type: 'text/plain',
               value: req.body.text,
            },
         ],
      },
   };

   try {
      await Sendgrid.request(request);
   } catch (err) {
      next(err);
      return;
   }

   res.send('POST is sended.');
});

if (module === require.main) {
   const PORT = process.env.PORT || 8080;
   app.listen(PORT, () => {
      console.log(`App listening on port ${PORT}`);
      console.log('Press Ctrl+C to quit.');
   });
}

module.exports = app;

上記 app.js を実行するには、下記のモジュールを追加しておく必要があります。

npm install --save express
npm install --save body-parser
npm install --save @sendgrid/client

"app.use(bodyParser.json());" を記載することで、JSON データでの POST 送信を受け取れるようにし、key が "email"、"sub"、"text" で送られてきた値を、メールの"送信先"、"件名"、"本文"内容にあてています。

また、package.json も修正が必要で、あらかじめ登録してある内容に下記コードを追加する必要があります。

package.json(追加)
  "main": "app.js",
  "scripts": {
    "start": "node app.js"
  }

以上、完成したら下記コマンドで GAE にデプロイを行います。

gcloud app deploy app.flexible.yaml

今回は、yaml ファイルに "flexible" と付けているので、ファイル名指定してデプロイしてください。

※ gcloud コマンドに関しては、Cloud SDK を参照してください。

デプロイが完了したら、いよいよリクエストの送信です。

5. JSON データを POST 送信

GAE の Web サーバーに対して、JSON データを POST します。

GAE がデプロイされると、"appspot.com" の URL が発行されます。今回 POST のエンドポイントを "/hello" と指定したので、下記の内容で POST 送信を行います。

エンドポイント
https://sendgridtestXXXXXXXX.appspot.com/hello

メソッド
POST

JSON データ
{
  "email":"dokiXXXnjc.co.jp",
  "sub":"テスト",
  "text":"テストメールです"
}

Chrome 拡張の送信ツールでも、curl でもなんでもいいので上記内容を送信します。私は "Postman" を使いました。

Screen Shot 2019-11-28 at 17.28.16.png

自分から自分にメールを送信しているので、結果下記のようにメール送信が行われました。

Screen Shot 2019-11-28 at 17.28.35.png

yaml ファイルの環境変数に設定した "dokiXXXnjc.co.jp" さんから、JSON の email に設定した"dokiXXXnjc.co.jp(To自分)"さんに送られています。

まとめ

SendGrid のライブラリがあるので、実装自体簡単にできますね!

SendGrid では、リアルタイム情報の取得が可能で、メールの送信だけでなく、メールの受信、webhook を使用して送信したメールの確認を行うことができます。機能は API でも用意されているので、"メール開封率の確認"や"メール受信をトリガーとして処理実行"などを他のシステムに組み込み、統計取得やその可視化などに利用できそうです。

Event API Inbound Parse API

SendGrid と Amazon SES とを軽く比べてみると(単純に比較できるものではないですが)、1 通あたりの値段は SendGrid の方が高いですね(無料で利用できる範囲も Amazon SES が 60,000 と多い)。ただ SendGrid には、メール開封の確認ができるといったことがデフォルトでできるという強みがあるようです。

メール配信サービスに関する良い比較サイトがあったので参照して確認してみてください。
SocialCompare - Best Transactional Email Service Comparison

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Node.jsで作成したWebアプリをAzureで公開する【2019年11月版】

はじめに

Node.jsでWebアプリを作ってみた、ローカルでは動作確認した、方が初めてAzureへ公開するための手順説明です。以前の次の記事の、AzureポータルのUI変更に伴う手順更新版、です。

Visual Studio連携や、Visual Code連携ではなく、GitHubのリポジトリを元に
公開する方法を説明します。
リポジトリ種別は、パブリックでもプライベートでもどちらでも問題ありません。

前準備

次を前提とします。

  • Azureのアカウントは作成済み
  • ローカルで動作確認済みのWebアプリ(Node.js)をGitHubのリポジトリに格納済み

Azureのアカウントの新規作成については、今でもそんなに変わっていないハズなので、
下記の記事の「Azureアカウントの作成方法」を参照ください。

Web APP(Paas)の捉え方

本節は、私自身の理解のための「捉え方の説明」です。「手順が知れればよい」方は次の節へ進みください。

ローカルで、動作確認済みのWebアプリ(Node.js)があったとします。
それはつまり、
「Httpサーバーをローカルで起動して、そのローカルサーバーにブラウザでアクセスすると、作成したWebアプリが動作する」
という状態です。

ここをスタート地点として、その作成済みのWebアプリを外部(インターネット上)に公開するためには何が必要でしょうか? 必要なのは次の手順でしょう。

  1. 外部公開用のマシンを用意する
    • 「サーバー」と呼ばれるもの。
  2. マシンにOSをインストールする、
  3. OS上に動作環境(Node.js)をセットアップする
  4. 外部(インターネット)からのアクセスを受け入れるようにルーター、Firewallを設定する
  5. FQDNでアクセスできるように、DNSを設定してIPと紐づける
  6. Webアプリのソースを格納して、起動する

AzureのWebアプリ(Paas)で公開する、とは上記と同じことを「仮想的に行う」ことを意味します。実際、Azure上で公開するまでに、Azureポータルで次の操作をします。

  1. 外部公開用のインスタンス(入れ物、枠、くらいに捉える)を用意する
    • 仮想マシン、などと呼ばれるもの
    • Azureポータル上では「リソース」とも呼ばれる
  2. インスタンスで利用するOSを選択する
    • Azureポータル上では「インスタンスの詳細>オペレーティングシステム」で表示される
  3. インスタンスに導入する動作環境(Node.js)を選択する
    • Azureポータル上では「インスタンスの詳細>ランタイムスタック」で表示される
  4. (外部からのアクセスを受け入れるように~、の設定はイイ感じにAzure側が設定してくれる)
  5. (FQDNで~、の設定はデフォルトに従ってAzure側で実施してくれる)
  6. Webアプリのソースを格納する(と、Azureが起動してくれる)

では、次の節で具体的な操作方法を説明します。

Web APPとしての公開するまでの手順

本記事では、OS+Webアプリの動作環境までをAzure側に任せる(マネージド)形式での公開方法(Paas)を説明します。

ソース配置するための仮想マシン(リソース、Paas)を作成する

  1. Azureポータルにログインする
  2. 「Azureサービス>リソースの作成>新規>Web>Webアプリ」と辿る リソースの作成 Webアプリ
  3. Webアプリのインスタンス設定画面が表示されるので、先ずはサブスクリプションとリソースグループを選択する
    • サブスクリプションは、どの料金体系を利用するか?の選択
      • 通常は「従量制」を選ぶ。
      • 利用開始から12 か月の無料サービス中は、そちらを選ぶ。
    • リソースグループは、リソースのグルーピング。同じ部屋にあるマシン、くらいに捉えればよい。
      • 後で「この部屋にあるマシン全部廃棄(リソースグループを削除)」とかする際に楽。
      • 初回は、「1つ目の置き部屋」くらいに捉えて、好きなな名前を入力する
      • 二回目以降は、既存と新規の好きな方から選ぶ Webアプリの属するサブスクリプションとリソースグループを選択
  4. インスタンスの名前を入力する
    • 公開時のデフォルトURLに組み込まれる
    • インスタンス名称を「xxx」とすると、「https://xxx.azurewebsite.net」になる
  5. 利用するOSと、動作環境を選択する
    • ランタイムスタック:動作環境を選択
      • Node xxx から好きなバージョンを選ぶ。
      • 当方は過去に作成済み環境に合わせたかったので、「Node 10.14」を選択
    • OS:WindowsかLinuxを選ぶ
      • ランタイムスタックによっては、選べないOSもある
      • 当方は、過去の環境に合わせたかったので、「Windows」を選択
    • 地域:仮想マシンが格納される実際の地域を選ぶ
      • 地域によってある程度の金額差はあるが、特にこだわりが無ければ「Japan East」か「Japan West」で良いかと インスタンス名称を入力し、動作環境を選択
  6. App Serviseプランを選ぶ
    • 仮想マシンのスペックを選ぶ。
    • 作成済みの「App Serviseプラン」がある場合は、目的に合致するスペックの者を選ぶ。
      • 先の「ランタイムスタック」と「OS」が同じ既存のプランがある場合には、リストされる。
    • 初回は適当に名前を付けて新規作成する。 App Serviseプランを新規作成する
      • 新規作成時は「App Serviseプラン>SKUとサイズ>サイズを変更します」から選択する
      • デフォルトでは「Standard S1」が選択されている
      • お試しで公開してテストしたい方は「F1(無料)」を選ぶのがおススメ。
        • 「サイズを変更します」を押した先で「スペックの選択>開発/テスト>F1(無料)」を選択できる デフォルトはS1(有料) 初期表示は運用タブ 開発/テストタブを選択するとF1がある F1を選択した状態
  7. 下部の「次:監視>」ボタンを押す
  8. Application Insightsを設定する
    • 設定は任意
      • 転送量やアラート通知先などを考えるのが面倒なので、ここでは利用を「いいえ」とする
      • 後から簡単に追加できる Application insightsは「いいえ」を選択
    • 「はい」を選ぶと、次の値(メトリックス)をデフォルトで監視できる
      • 要求レート、応答時間、およびエラー率
      • 依存率、応答時間、およびエラー率:
      • 例外
      • ページ ビューと読み込みのパフォーマンス
      • Web ページからの AJAX 呼び出し
      • ユーザー数とセッション数。
        • Windows または Linux サーバー コンピューターの CPU、メモリ、ネットワーク使用率などのパフォーマンス カウンター。
      • Docker または Azure のホスト診断
      • アプリの診断トレース ログ
      • カスタム イベントとメトリック。
    • 詳細はこちら
  9. 下部の「次:タグ>」ボタンを押す
    • 設定は任意。
      • ここでは特に設定しない タグは設定しない
  10. 構成及び確認で、問題なければ「作成」ボタンを押す 内容を確認後に、作成ボタンを押す
    • デプロイが進行中、の画面に続いて、完了表示が出るまで暫し待つ。 デプロイ中 デプロイ完了
  11. 「リソースに移動」ボタンを押すと、Webアプリのリソース概要の画面に切り替わる リソースの概要

GitHubを紐づけてソースを配置する

続いて、ソースファイルをGitHubから紐づけて、今しがた作成したPaasの上にWebアプリとして公開します。

  1. リソースの概要が表示された状態で「デプロイメント>デプロイセンター」に入る
    • 普段は「ホーム>全てのリソース>名前(作成したWebアプリの名称を選ぶ)」から辿れます。
  2. デプロイセンター内で「GitHub」を選択する GitHubを選択
  3. 初回の場合はGitHubアカウント選択/2回目以降で変更したい場合は下部の「アカウントの変更」ボタンを押す GitHubのアカウントを選択
  4. GitHubアカウントの入力画面が出るので、IDとパスワードを入力して「Sign in」ボタンを押す(AOuth認証する) GitHubに紐付ける
  5. ビルドプロバイダーの選択:変更せずにそのまま「続行」ボタンを押す ビルドプロバイダーはデフォルトのまま
  6. 構成画面で、ドロップダウンリストから先ほど紐付けたGitHubのでリポジトリとブランチを選択する デプロイするGitHubのリポジトリとブランチを指定
  7. 概要画面で確認したら、「完了」ボタンを押す 概要を確認して完了ボタンを押す
  8. しばし待つとデプロイ成功のメッセージが出る デプロイ成功
  9. Webアプリのリソース概要画面に戻り、URLをクリックすると、公開したWebアプリの頁に飛べる デプロイ後のリソース概要画面

飛んだ先で期待したように動作することを確認して、Webアプリの公開は完了。

以上ー。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

nodeを使ってmysqlに接続する

nodeでmysqlに接続するには

// requireの設定
const mysql = require('mysql');

// MySQLとのコネクションの作成
const connection = mysql.createConnection({
  host : 'localhost',
  user : 'root',
  database: 'testdatabase'
});

// 接続
connection.connect();

// userdataの取得
connection.query('SELECT * from userdata;', function (err, rows, fields) {
  if (err) { console.log('err: ' + err); } 

  console.log('name: ' + rows[0].name);
  console.log('id: ' + rows[0].id);

});

// userdataのカラムを取得
connection.query('SHOW COLUMNS FROM userdata;', function (err, rows, fields) {
  if (err) { console.log('err: ' + err); }

  console.log(rows[0].Field);
  console.log(rows[1].Field);
});

// 接続終了
connection.end();
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む