20200515のNode.jsに関する記事は9件です。

Vuetfiy v-dialogのモーダル化が機能しない

Vuetify v-dialogのモーダル化が機能しない

新規で作成もしくはソースコードの内容を大体把握してある個人の方・一人でプロジェクトを回している方には参考にならないかと思います。

Vuetifyのv-dialogにはオプションで色々指定できる
モーダル化させるためにpersistentを指定することでダイアログ外を押しても閉じないようにできる

それが機能しなかったので備忘録的にまとめる

Vuetify
https://vuetifyjs.com/ja/components/dialogs/

結論

outsideというオプションが悪さしていました。
このオプションはダイアログ外を押した時に発火するイベントです。
そいつが諸悪の権化なので削除するなりよしなに修正することで回避しましょう。

こんなことで時間取られるの辛い…辛い…

ちなみに調べるとVuetify2.1以下だとバグで機能しないらしいですが、orverlayと合わせるとバグるみたいなので多分今回の場合は関係ないです。
https://github.com/vuetifyjs/vuetify/issues/8697

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

Kubernetes基礎(3):Overview

Kubernetes Overview

Kubernetes_overview.png
Kubernetes Cluster
サーバー1台はMasterとして使って、他の複数のサーバはNodeとしてMasterに接続します。
このような組み合わせで接続されているのをKubernetes Clusterと言います。
※Nodeは3つ以上を推薦しています。

Master
Clusterを管理します。

Node
リソースを提供します。
Cluster全体のリソースを増やしたい場合はNodeを追加します。
Nodeにはkubeletが存在し、Kubernetes Masterと通信(using kubernetes API)、Nodeを管理します。
(kubeletをagentとも呼びます。)

Namespace
Cluster内のNamespaceがKubernetesのオブジェクトを独立した空間に分離(=隔離)します。
NamespaceはKubernetesの最小配布単位のPodがあり、このPodは外部からの接続が可能なように
IPアドレスを割り当てたServiceがあって接続が可能になります。
※別のNamespaceのPodへの接続はできない。

Pod
PodはKubernetes Applicationの基本実行単位であり、Deployする最小単位です。
Podには複数のContainerが入れます。
Container毎に1つのApplicationが動作するため、Podは複数のApplicationの稼働ができます。

Service
Kubernetesで動かしているサービス(PodのContainer)を外部公開します。
Podに含まれているContainerのtraffic load balancingをサポートします。

Volume
Podに問題が生じて再生成されると、その中のデータは消えてしまいます。
Volumeを作成して繋げておけば、データはVolumeに保管されるのでPodが再生成されても、データは消えません。

ResourceQuota/LimitRange
1つのNamespaceで使えるリソースの制御ができます。
例)Pod数、CPU、Memory

ConfigMap/Secret
Podの作成時、Containerの環境変数などを設定したファイルへのMountができます。

Controller
Podを管理する。
・Replication Controller/ ReplicaSet(基本的なController)
 Podの状況を常に監視し、Podが多すぎるとPodを除去し、少ないとPodを開始したり、Podを管理します。
・Deployment
 Application Instanceの生成と更新を担当します。
 Podのバージョン管理を行います。
 アップグレード時、問題が発生した場合はロールバックもできます。
・DaemonSet
 1つのNodeに1つのPodが維持出来るように管理します。
・Job
 1つ以上のPodを作成し、指定された数のPodが正常に終了することを保証します。
 このJobを定期的に実行するときはCronJobを使います。

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

webpack 4系でwebpack.config.jsの自動生成ができないときの解決方法

株式会社ONE WEDGEでエンジニアをしている @YoukeyMurakami です。

プロジェクト開始時、今までwebpack.config.jsは手でシコシコ書いていたんですけど、「自動生成出来るよ」って聞いたので試してみたときの備忘録になります。

環境

結果的にOKだった環境は次の通り

name version
node.js 13.8.0
webpack 4.43.0
webpack-cli 3.31.0
@webpack-cli/init 0.2.2

失敗したケース

# 各種ツール類のインストール
$ yarn add -D webpack webpack-cli @webpack-cli/init

# webpack.config.jsの自動生成
$ webpack init
/Users/YoukyMurakami/project_hoge/node_modules/@webpack-cli/utils/npm-packages-exists.js:42
            throw new TypeError(chalk_1.default.bold(`${scaffold} isn't a valid name.\n`) +
            ^

TypeError: init isn't a valid name.

It should be prefixed with 'webpack-scaffold', but have different suffix.

    at /Users/YoukyMurakami/project_hoge/node_modules/@webpack-cli/utils/npm-packages-exists.js:42:19
    at Array.forEach (<anonymous>)
    at Object.npmPackagesExists [as default] (/Users/YoukyMurakami/project_hoge/node_modules/@webpack-cli/utils/npm-packages-exists.js:26:9)
    at initializeInquirer (/Users/YoukyMurakami/project_hoge/node_modules/@webpack-cli/init/index.js:23:41)
    at runWhenInstalled (/Users/YoukyMurakami/project_hoge/node_modules/webpack-cli/bin/utils/prompt-command.js:46:9)
    at promptForInstallation (/Users/YoukyMurakami/project_hoge/node_modules/webpack-cli/bin/utils/prompt-command.js:140:10)
    at /Users/YoukyMurakami/project_hoge/node_modules/webpack-cli/bin/cli.js:32:43
    at Object.<anonymous> (/Users/YoukyMurakami/project_hoge/node_modules/webpack-cli/bin/cli.js:366:3)
    at Module._compile (internal/modules/cjs/loader.js:1151:30)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:1171:10)

oh...TypeError: init isn't a valid name.って出ていますね。
ちょっとググってみると、どうやらinitじゃなくてcreateになるよってことらしいのですが、createを使うにはwebpack-cli@betaをインストールしてねということだそうです。
webpack-cli本体のバージョンは上げずになんとかするには・・・

解決策

webpack-cliとwebpackのバージョンの組み合わせによりうまく動かないようなので、以下のコマンドでダウングレードしたwebpack-cliをインストールして下さい。

# 0.2.2バージョン指定でインストール
$ yarn add -D webpack webpack-cli@0.2.2
$ webpack init
INFO  For more information and a detailed description of each question, have a look at: https://github.com/webpack/webpack-cli/blob/master/INIT.md
INFO  Alternatively, run "webpack(-cli) --help" for usage info

? Will your application have multiple bundles? (y/N) Run-async wrapped function (sync) returned a promise but async() callback must be executed to resolve.

無事に動きました!
最終的なpackage.jsonを貼っておきます。

package.json
{
    "name": "hoge",
    "description": "hogehoge",
    "version": "0.0.1",
    "private": true,
    "dependencies": {
    },
    "devDependencies": {
        "webpack": "^4.43.0"
        "@webpack-cli/init": "0.2.2",
    },
    "scripts": {
        "test": "jest"
    }
}

仲間募集!

株式会社ONE WEDGEでは元気なエンジニア募集中です!一緒に「おもしろい」を作りましょう!

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

爆速構築!json-serverでMock API

はじめに

フロントエンド開発のためにAPIのモックが必要になったので、json-serverを利用してモックサーバを構築していきます。

基本的な環境構築

初期化

powershell
# プロジェクトディレクトリを作成し, 移動する
mkdir mock-api
cd ./mock-api

# 初期化処理
npm init -y

json-server

今回のメインとなる json-server を導入します。

powershell
npm i -D json-server

contents.json

エンドポイントとなる json ファイルを作成します。

powershell
mkdir api
new-item api/contents.json

作成した contents.json を編集します。
ここで編集した内容がAPIのレスポンスとして返されるようになります。

contents.json
{
    "contents": [
        {
            "id": 1,
            "title": "title - foo",
            "body": "body - bar",
            "author-id": 1
        },
        {
            "id": 2,
            "title": "title - foo2",
            "body": "body - bar",
            "author-id": 1
        },
        {
            "id": 3,
            "title": "title - bar",
            "body": "body - bal",
            "author-id": 2
        }
    ]
}

package.json

package.json を更新します。

package.json
{
  "name": "mock-api",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    ### ↓↓↓↓↓ ここから追加 ↓↓↓↓↓ ###
    "json-server": "json-server --watch ./api/contents.json --port 5000"
    ### ↑↑↑↑↑ ここまで追加 ↑↑↑↑↑ ###
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "json-server": "^0.16.1"
  }
}

実行

npm run コマンドで実行します。

powershell
npm run json-server

実行されると、以下にGET通信することで先ほどのcontents.jsonの内容が得られます。

http://localhost:5000/contents

応用的な環境構築

エンドポイントを複数用意したい場合

エンドポイントを複数用意したい場合、contents.jsonにもう一つ要素を追加することで解決します。

contents.json
{
    "contents": [
        {
            "id": 1,
            "title": "title - foo",
            "body": "body - bar",
            "author-id": 1
        },
        {
            "id": 2,
            "title": "title - foo2",
            "body": "body - bar",
            "author-id": 1
        },
        {
            "id": 3,
            "title": "title - bar",
            "body": "body - bal",
            "author-id": 2
        }
    ],
    "authors": [
        {
            "id": 1,
            "name": "hoge"
        },
        {
            "id": 2,
            "name": "fuga"
        }
    ]
}

上記のように編集し実行すると、以下のように通信先を切り替えることができます。

http://localhost:5000/contents
http://localhost:5000/authors

エンドポイント毎にファイルを分割したい場合

しかし、APIの規模が大きくなってくると単一ファイルではメンテナンスが難しくなってくることは想像に難しくありません。
そうなると「エンドポイント毎にファイルを分割したい」という欲求が生まれてきます。

そこで問題となってくるのが json-server の「単一ファイルしか受け付けない」という仕様です。
なので、今回は複数ファイルを一つにマージすることで対応したいと思います。

【参考】
  - Json-serverでモックAPI

(1) まず、マージをするためのスクリプトを用意します

powershell
mkdir scripts
new-item merge.js
scripts/merge.js
const path = require("path");
const fs = require("fs");

const root = path.resolve("./", "api");
const update = () => 
{
    const api = fs.readdirSync(root).reduce((api, file) => {
      if (api === undefined)
        api = {};

      if (path.extname(file) == ".json") {
        const endpoint = path.basename(file, path.extname(file));

        if (api[endpoint] === undefined)
          api[endpoint] = {};

        api[endpoint] = JSON.parse(fs.readFileSync(root + "/" + file, "utf-8"));
        return api;
      }
    }, {});

    fs.writeFile(root + "/../merged.json", JSON.stringify(api), err => {
      if (err)
          throw err;
    });
}

// 初回作成
update();

// jsonファイルを監視し, 監視ファイルに更新があるたびmerged.jsonを更新
fs.watch(root, (e, filename) => update());

(2) json-servermerge.js を両方同時に動作させるために npm-run-all を導入します

powershell
npm i -D npm-run-all

(3) package.json を更新します

package.json
{
  "name": "mock-api",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    ### ↓↓↓↓↓ ここから追加・更新 ↓↓↓↓↓ ###
    "json-server": "json-server --watch merged.json --port 5000",
    "merge": "node ./scripts/merge.js",
    "serve": "npm-run-all -p merge json-server"
    ### ↑↑↑↑↑ ここまで追加・更新 ↑↑↑↑↑ ###
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "json-server": "^0.16.1"
  }
}

(4) contents.jsoncontents.jsonauthors.json に分割します

powershell
new-item api/authors.json
contents.json
{
    "contents": [
        {
            "id": 1,
            "title": "title - foo",
            "body": "body - bar",
            "author-id": 1
        },
        {
            "id": 2,
            "title": "title - foo2",
            "body": "body - bar",
            "author-id": 1
        },
        {
            "id": 3,
            "title": "title - bar",
            "body": "body - bal",
            "author-id": 2
        }
    ]
}
authors.json
{
    "authors": [
        {
            "id": 1,
            "name": "hoge"
        },
        {
            "id": 2,
            "name": "fuga"
        }
    ]
}

(5) 以下のコマンドで実行します

powershell
npm run serve

これで複数ファイルに分割定義することが可能となります。

POSTやPUTでもレスポンスを受け取りたい場合

json-serverGET以外 のリクエストだと思ったようにレスポンスを返してくれないので、処理をフックして GET通信 に偽装します。

【参考】
  - json-server で使い捨てモックサーバを作る
  - JSON Serverを使ってGETとPOSTでレスポンスを変えてみた

(1) まず、処理をフックするためのスクリプトを追加します

powershell
new-item ./scripts/middleware.js
middleware.js
module.exports = (req, res, next) =>
{
    if(req.method == 'POST') {
        req.method = 'GET'      // GETに偽装
        req.query = req.body
    }
    next()
}

(2) package.json を更新します

以下では、--middlewares オプションを追加しています。

package.json
{
  "name": "mock-api",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    ### ↓↓↓↓↓ ここから更新 ↓↓↓↓↓ ###
    "json-server": "json-server --watch merged.json --port 5000 --middlewares ./scripts/middleware.js",
    ### ↑↑↑↑↑ ここまで更新 ↑↑↑↑↑ ###
    "merge": "node ./scripts/merge.js",
    "serve": "npm-run-all -p merge json-server"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "json-server": "^0.16.1"
  }
}

これでPOST通信でもjsonレスポンスを得ることができます。

APIのrouteをカスタムしたい場合

現状、APIのrouteが http://localhost:5000/{jsonファイル名} となっているため、これを http://localhost:5000/api/v1/{jsonファイル名} となるように変更してみたいと思います。

【参考】
  - Json-serverでモックAPI

(1) まず、routes.json を追加します

powershell
new-item ./routes.json
routes.json
{
  "/api/v1/*": "/$1"
}

(2) package.json を更新します

以下では、--routes オプションを追加しています。

package.json
{
  "name": "mock-api",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    ### ↓↓↓↓↓ ここから更新 ↓↓↓↓↓ ###
    "json-server": "json-server --watch merged.json --port 5000 --routes ./routes.json --middlewares ./scripts/middleware.js",
    ### ↑↑↑↑↑ ここまで更新 ↑↑↑↑↑ ###
    "merge": "node ./scripts/merge.js",
    "serve": "npm-run-all -p merge json-server"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "json-server": "^0.16.1"
  }
}

これでhttp://localhost:5000/api/v1/{jsonファイル名}でアクセスできるようになります。

おわりに

さらに詳しくは 公式サイト をご参照ください。

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

Bitriseのステータスをモニターに表示して物理的に監視する

背景

  • Bitriseのワークフローが成功/失敗したらSlackに通知する仕組みはよくある
  • 物理的にモニターにステータスを表示するツールの紹介はあまり見ない

ツール

https://github.com/marcells/node-build-monitor
こんな感じで表示される
node-build-monitor.png
https://builds.mspi.es/ にデモがあるので見てみてください。
表示形式の変更や、失敗したら音を鳴らすなどの設定も可能です。

やってみる

GitHubのREADMEで詳細は記載されているので、ここではBitriseを使用したハッピーパスだけ紹介します。

{
  "monitor": {
    "interval": 300000,
    "numberOfBuilds": 12,
    "latestBuildOnly": false,
    "sortOrder": "date",
    "debug": false
  },
  "services": [
    {
      "name": "Bitrise",
      "configuration": {
        "slug": "BitriseでのアプリID",
        "token": "パーソナルアクセストークン"
      }
    }
  ]
}

最後に

Slackの通知でも気づくことはもちろんできますが、オフィスのサイネージなどで表示しておけばエンジニアだけでなく、PMやデザイナーなどのプロジェクトメンバー全員がCIのステータスを気にする意識が持てるので、そこから何かいいアクションに繋がればいいなと思います。

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

Bitriseのステータスをモニターに表示して物理的に監視するツールの紹介

背景

  • Bitriseのワークフローが成功/失敗したらSlackに通知する仕組みはよくある
  • 物理的にモニターにステータスを表示するツールの紹介はあまり見ない
  • 複数のCIサービスのステータスを1画面でみたい

ツール

https://github.com/marcells/node-build-monitor
こんな感じで表示される
node-build-monitor.png
https://builds.mspi.es/ にデモがあるので見てみてください。
表示形式の変更や、失敗したら音を鳴らすなどの設定も可能です。

やってみる

GitHubのREADMEで詳細は記載されているので、ここではBitriseを使用したハッピーパスだけ紹介します。

{
  "monitor": {
    "interval": 300000,
    "numberOfBuilds": 12,
    "latestBuildOnly": false,
    "sortOrder": "date",
    "debug": false
  },
  "services": [
    {
      "name": "Bitrise",
      "configuration": {
        "slug": "BitriseでのアプリID",
        "token": "パーソナルアクセストークン"
      }
    }
  ]
}

最後に

Slackの通知でも気づくことはもちろんできますが、オフィスのサイネージなどで表示しておけばエンジニアだけでなく、PMやデザイナーなどのプロジェクトメンバー全員がCIのステータスを気にする意識が持てるので、そこから何かいいアクションに繋がればいいなと思います。

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

耳年齢判定ボットを改良(LINEで音声ファイルを再生)

概要

普段は耳鼻科の開業医をしています。
以前obnizeのスピーカーからモスキート音を出し加齢性難聴をチェックするLINE Botを作成しました。
耳年齢を判定するLINE Bot×Iotの作成

今回、モスキート音をファイルに入れ、LINEで音声ファイルを再生できるようにしました。

作成方法

1.モスキート音を用意する

こちらを利用しました
Sine Tone Generator

『File Generator』の
『Hz』をモスキート音の周波数に設定、『duration』は3秒とし『DOWNROAD.WAV FILE』をクリックするとダウンロードできます。
各周波数分用意します。

image.png

ダウンロードしたファイルを再生するとサイン音が聞こえます。
image.png

2.publicフォルダを作ってwavファイルを設置

image.png

3.コードの追加

const config = {
    channelSecret: process.env.CHANNEL_SECRET,
    channelAccessToken: process.env.CHANNEL_ACCESS_TOKEN
};

const app = express();
app.use(express.static('public'));  //追加

3.wavファイルのURLを動的に取得する
app.post('/webhook'......内の処理を書き換える

Promise
      .all(req.body.events.map(event=>handleEvent(event,req)))
      .then((result) => res.json(result));

function handleEvent(event) { に引数を追加

function handleEvent(event, req) {
  console.log(req);
  if (event.type !== 'message' || event.message.type !== 'text') {
    return Promise.resolve(null);
  }

メッセージ内にURLを入れる

let url;
  if (hz == 0) {
    url ="";
  } else if (hz == 8000) {
    url ="デプロイしたボットのURL/public/8000.wav";
  } else if (hz == 10000) {
    url ="デプロイしたボットのURL/10000.wav";
  } else if (hz == 12000) {
    url ="デプロイしたボットのURL/12000.wav";
  } else if (hz == 14000) {
    url ="デプロイしたボットのURL/14000.wav";
  } else if (hz == 15000) {
    url ="デプロイしたボットのURL/15000.wav";
  } else if (hz == 16000) {
    url ="デプロイしたボットのURL/16000.wav";
  }

LINEで複数のメッセージを返信する

 replyMessage(event.replyToken, [    
 { type: "text", text: "第一のメッセージ" },    
 { type: "text", text: '第二のメッセージ' }   
]) 

完成

image.png
image.png
image.png

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

Windows上でSQL Serverを使用してNode.jsアプリを作成する

はじめに

この記事では、Microsoft 社が公開している Build an app using SQL Server の内容に従い、SQL Server を使用した Node.js アプリを作成します。

第30回 SQL Server 2019 勉強会@JSSUG (2020/5/16) のセッション資料です。

環境

  • OS: Windows 10 Pro 10.0.19041 N/A ビルド 19041
  • SQL Server: SQL Server 2019
  • Node.js: v12.16.1

環境のセットアップ

SQL Server のインストール

ウェブ上では、ホスト OS に SQL Server 2017 Developer 以上をインストール とあります。
[こちら][SQLServerDownload] のサイトより、SQL Server 2019 Developer インストーラーをダウンロードし、インストールを行ってください。

筆者は、WSL2 (Ubuntu 18.04 LTS) 上にインストールした Docker 上に、SQL Server 2019 on Linux をインストールして利用しました。docker-compose.yaml については、以下の GitHub リポジトリを参考にしてください。構築後、ifconfig コマンドを実行し、eth0 の IP アドレスを確認してください。

Chocolatey と Node.js のインストール

既に Node.js をインストール済みの場合は、スキップして問題ありません。まだ Node.js をインストールしていない場合は、インストールを行ってください。
サイトでは、Chocolatey を使った Node.js のインストールについて説明されています。Chocolatory とは、Windows 版パッケージ管理マネージャーです。Ubuntu でいう apt-get、RHEL/CentOS でいう yum に相当します。

なお、筆者は docker 上に作成した Node.js コンテナを使用しました。先ほど作成した SQL Server on Linux コンテナと Node.js コンテナ同士が接続できるようにネットワーク設定を行えば、以降の作業を Docker 上で行うことも可能です。

Chocolateyをインストール
@powershell -NoProfile -ExecutionPolicy Bypass -Command "iex ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1'))" && SET "PATH=%PATH%;%ALLUSERSPROFILE%\chocolatey\bin"
ChocolateyでNode.jsをインストール
choco install -y nodejs

SQLCMD のインストール

SQLCMD は、SQL Server に接続してクエリを実行できるコマンドラインツールです。まだインストールしていない場合は、以下の手順に従ってインストールを行ってください。

  1. ODBC Driver for SQL Server をダウンロードし、インストール
  2. sqlcmd ユーティリティ をダウンロードし、インストール

インストールが完了したら、SQLCMD を利用して SQL Server に接続できることを確認します。

sqlcmd -S <接続するSQL Serverインスタンス名> -U sa -P <saユーザーパスワード> -Q "SELECT @@VERSION"

SQL Server を使った Node.js アプリケーションを作成

ここでは、以下、2 つのシンプルな Node.js アプリを作成します。

  • 基本的な Insert、Update、Delete、Select を実行するアプリ
  • Node.js の ORM の中でも特に人気のある Sequelize を利用してInsert、Update、Delete、Select を実行するアプリ

SQL Server に接続してクエリを実行する Node.js アプリを作成

まずはじめに、Node.js プロジェクトを初期化します。

# 作業を行うフォルダに移動
cd ~/
# アプリ用のフォルダを作成
mkdir SqlServerSample
cd SqlServerSample
# npm パッケージをセットアップ
npm init -y
# プロジェクトフォルダに tedious と async モジュールをインストール
npm install tedious 
npm install async 

プロジェクトの初期化が完了したら、sqlcmd を使用してSQL Serverに接続します。
以下のステートメントを実行して、今回使用するデータベースを作成します。

sqlcmd -S <接続するSQL Serverインスタンス名> -U sa -P <saユーザーパスワード> -Q "CREATE DATABASE SampleDB;"

準備ができたところで、アプリを作成していきます。
お気に入りのエディタを使って、SqlServerSample フォルダ内に connect.js というファイルを作成します。
ユーザー名とパスワードは自分のものに置き換えることを忘れないでください。
なお、筆者は、Visual Studio Code を利用しています。

connect.js
var Connection = require('tedious').Connection;
var Request = require('tedious').Request;
var TYPES = require('tedious').TYPES;

// データベースへのコネクション情報を作成
var config = {
  server: 'localhost',              // 接続先の SQL Server インスタンス
  authentication: {
      type: 'default',
      options: {
          userName: 'sa',           // 接続ユーザー名
          password: 'your_password' // 接続パスワード
      }
  },
  options: {
      database: 'SampleDB'          // 接続するデータベース(ここは変えないでください)
  }
}
var connection = new Connection(config);

// コネクション情報を使用して、接続とクエリ実行
connection.on('connect', function(err) {
  if (err) {
    console.log(err);
  } else {
    console.log('接続されました。');
  }
});

実際に connect.js を動かします。

node connect.js

次に、SqlServerSample フォルダ内に CreateTestData.sql というファイルを作成します。
このファイルでは、T-SQL を使用して、SampleDB 内にスキーマおよびテーブルが作成を作成し、データを登録します。

CreateTestData.sql
CREATE SCHEMA TestSchema;
GO

CREATE TABLE TestSchema.Employees (
  Id INT IDENTITY(1,1) NOT NULL PRIMARY KEY,
  Name NVARCHAR(50),
  Location NVARCHAR(50)
);
GO

INSERT INTO TestSchema.Employees (Name, Location) VALUES
(N'Jared', N'Australia'),
(N'Nikita', N'India'),
(N'Tom', N'Germany');
GO

SELECT * FROM TestSchema.Employees;
GO

SQLCMD を使って CreateTestData.sql を実行します。

sqlcmd -S <接続するSQL Serverインスタンス名> -U sa -P <saユーザーパスワード> -d SampleDB -i ./CreateTestData.sql

SqlServerSample フォルダ内に crud.js という新しいファイルを作成します。
Insert、Update、Delete、Select を行う処理を記載していきます。
ユーザー名とパスワードは自分のものに置き換えることを忘れないでください。

crud.js
var Connection = require('tedious').Connection;
var Request = require('tedious').Request;
var TYPES = require('tedious').TYPES;
var async = require('async');

// データベースへのコネクション情報を作成
var config = {
  server: 'localhost',              // 接続先の SQL Server インスタンス
  authentication: {
      type: 'default',
      options: {
          userName: 'sa',           // 接続ユーザー名
          password: 'your_password' // 接続パスワード
      }
  },
  options: {
    database: 'SampleDB'            // 接続するデータベース(ここは変えないでください)
  }
}

var connection = new Connection(config);

function Start(callback) {
    console.log('開始しています...');
    callback(null, 'Jake', 'United States');
}

function Insert(name, location, callback) {
    console.log("テーブルに '" + name + "' を追加しています...");

    request = new Request(
        'INSERT INTO TestSchema.Employees (Name, Location) OUTPUT INSERTED.Id VALUES (@Name, @Location);',
        function(err, rowCount, rows) {
        if (err) {
            callback(err);
        } else {
            console.log(rowCount + ' 行 追加されました。');
            callback(null, 'Nikita', 'United States');
        }
        });
    request.addParameter('Name', TYPES.NVarChar, name);
    request.addParameter('Location', TYPES.NVarChar, location);

    // SQL ステートメントを実行
    connection.execSql(request);
}

function Update(name, location, callback) {
    console.log("Location '" + location + "' の name '" + name + "' を更新しています...");

    // 依頼された従業員の記録を更新
    request = new Request(
    'UPDATE TestSchema.Employees SET Location=@Location WHERE Name = @Name;',
    function(err, rowCount, rows) {
        if (err) {
        callback(err);
        } else {
        console.log(rowCount + ' 行 更新されました。');
        callback(null, 'Jared');
        }
    });
    request.addParameter('Name', TYPES.NVarChar, name);
    request.addParameter('Location', TYPES.NVarChar, location);

    // SQL ステートメントを実行
    connection.execSql(request);
}

function Delete(name, callback) {
    console.log("テーブルから '" + name + "' を削除しています...");

    // 要求された従業員の記録を削除
    request = new Request(
        'DELETE FROM TestSchema.Employees WHERE Name = @Name;',
        function(err, rowCount, rows) {
        if (err) {
            callback(err);
        } else {
            console.log(rowCount + ' 行 削除しました。');
            callback(null);
        }
        });
    request.addParameter('Name', TYPES.NVarChar, name);

    // SQL ステートメントを実行
    connection.execSql(request);
}

function Read(callback) {
    console.log('テーブルの行データを読み取っています...');

    // テーブルからすべての行を読み込む
    request = new Request(
    'SELECT Id, Name, Location FROM TestSchema.Employees;',
    function(err, rowCount, rows) {
    if (err) {
        callback(err);
    } else {
        console.log(rowCount + ' 行 読み込みました。');
        callback(null);
    }
    });

    // 読み込んだ行データの表示
    var result = "";
    request.on('row', function(columns) {
        columns.forEach(function(column) {
            if (column.value === null) {
                console.log('NULL');
            } else {
                result += column.value + " ";
            }
        });
        console.log(result);
        result = "";
    });

    // SQL ステートメントを実行
    connection.execSql(request);
}

function Complete(err, result) {
    if (err) {
        callback(err);
    } else {
        console.log("完了しました!");
    }
}

// コネクション情報を使用して、接続とクエリ実行
connection.on('connect', function(err) {
  if (err) {
    console.log(err);
  } else {
    console.log('接続されました。');

    // 配列内の全ての関数を連続的に実行
    async.waterfall([
        Start,
        Insert,
        Update,
        Delete,
        Read
    ], Complete)
  }
});

実際に、crud.js を動かします。

node crud.js

Sequelize ORM を使って SQL Server に接続する Node.js アプリを作成

アプリのフォルダを作成し、Node の依存関係を初期化します。

# 作業を行うフォルダに移動
cd ~/
# アプリ用のフォルダを作成
mkdir SqlServerSequelizeSample
cd SqlServerSequelizeSample
# npm パッケージをセットアップ
npm init -y
# プロジェクトフォルダに tedious と sequelize モジュールをインストール
npm install tedious
npm install sequelize

SqlServerSequelizeSample フォルダ内に orm.js という新しいファイルを作成します。
パスワードは自分のものに置き換えることを忘れないでください。

orm.js
var Sequelize = require('sequelize');
var userName = 'sa';            // 接続ユーザー名
var password = 'your_password'; // 接続パスワード
var hostName = 'localhost';     // 接続先の SQL Server インスタンス
var sampleDbName = 'SampleDB';  // 接続するデータベース(ここは変えないでください)

// Sequelize を初期化して SampleDB に接続
var sampleDb = new Sequelize(sampleDbName, userName, password, {
    dialect: 'mssql',
    host: hostName,
    port: 1433,     // 接続ポート番号
    logging: false, // ロギングを無効化(デフォルトは console.log)

    dialectOptions: {
        requestTimeout: 30000 // タイムアウトは 30 秒
    }
});

// 'User' モデルを定義
var User = sampleDb.define('user', {
    firstName: Sequelize.STRING,
    lastName: Sequelize.STRING
});

// 'Task' モデルを定義
var Task = sampleDb.define('task', {
    title: Sequelize.STRING,
    dueDate: Sequelize.DATE,
    isComplete: Sequelize.BOOLEAN
});

// User と Task は 1:N の関係
User.hasMany(Task);

console.log('**Sequelize と MSSQL を使った Node CRUD のサンプル**');

// データベース内のテーブルとリレーションシップを DROP および CREATE するよう Sequelize に指示
sampleDb.sync({force: true})
.then(function() {
    console.log('\nモデルからデータベーススキーマを作成.');

    // Create デモ: ユーザーインスタンスを作成し、データベースに保存
    User.create({firstName: 'Anna', lastName: 'Shrestinian'})
    .then(function(user) {
        console.log('\n作成されたユーザー:', user.get({ plain: true}));

        // Create デモ: タスクインスタンスを作成し、データベースに保存
        Task.create({
            title: 'Ship Helsinki', dueDate: new Date(2017,04,01), isComplete: false
        })
        .then(function(task) {
            console.log('\n作成されたタスク:', task.get({ plain: true}));

            // Association デモ: ユーザーにタスクを割り当てる
            user.setTasks([task])
            .then(function() {
                console.log('\n割り当てられたタスク \''
            + task.title
            + '\' to user ' + user.firstName
            + ' ' + user.lastName);

                // Read デモ: ユーザー 'Anna' に割り当てられた未完了のタスクを見つける
                User.findAll({
                    where: { firstName: 'Anna'},
                    include: [{
                        model: Task,
                        where: { isComplete: false }
                    }]
                })
                .then(function(users) {
                    console.log('\nAnna に割り当てられた未完了のタスク:\n',
                JSON.stringify(users));

                    // Update デモ: タスクの 'dueDate' を変更
                    Task.findById(1).then(function(task) {
                        console.log('\n更新中のタスク:',
                task.title + ' ' + task.dueDate);
                        task.update({
                            dueDate: new Date(2016,06,30)
                        })
                        .then(function() {
                            console.log('期限が変更されました。:',
                    task.title + ' ' + task.dueDate);

                            // Delete デモ: dueDate が 2016年 であるすべてのタスクを削除
                            console.log('\n期限が2016年になっているタスクをすべて削除しています。');
                            Task.destroy({
                                where: { dueDate: {$lte: new Date(2016,12,31)}}
                            })
                            .then(function() {
                                Task.findAll()
                                .then(function(tasks) {
                                    console.log('削除後のデータベース内のタスク一覧:',
                        JSON.stringify(tasks));
                                    console.log('\nすべて完了しました!');
                                })
                            })
                        })
                    })
                })
            })
        })
    })
})

実際に、orm.js を動かします。

node orm.js

これで、2つ目の Node.js アプリの作成が終わりました。最後に、SQL Server の Columnstore 機能を使って Node.js アプリを高速化する方法について学びます。

Node.js アプリを 100 倍速にする

これまでで基本的なことは理解できたと思います。最後は、SQL Server を使用してアプリをより良くする方法を見てみます。このモジュールでは、Columnstore Index の簡単な例と、Columnstore Index がどのようにデータ処理速度を向上させるかを確認します。Columnstore インデックスは、従来の rowstore インデックスに比べて、分析ワークロードでは最大 100 倍のパフォーマンス向上、データ圧縮では最大 10 倍のパフォーマンス向上を実現できます。

SQLCMD を使用して 500 万のデータを含む新しいテーブルを作成

アプリのフォルダを作成します。

# 作業を行うフォルダに移動
cd ~/
# アプリ用のフォルダを作成
mkdir SqlServerColumnstoreSample
cd SqlServerColumnstoreSample

SqlServerColumnstoreSample フォルダ内に CreateSampleTable.sql という名前の新しいファイルを作成します。

CreateSampleTable.sql
sqlcmd -S <接続するSQL Serverインスタンス名> -U sa -P <saユーザーパスワード> -d SampleDB -t 60000 -Q "WITH a AS (SELECT * FROM (VALUES(1),(2),(3),(4),(5),(6),(7),(8),(9),(10)) AS a(a))
SELECT TOP(5000000)
ROW_NUMBER() OVER (ORDER BY a.a) AS OrderItemId
,a.a + b.a + c.a + d.a + e.a + f.a + g.a + h.a AS OrderId
,a.a * 10 AS Price
,CONCAT(a.a, N' ', b.a, N' ', c.a, N' ', d.a, N' ', e.a, N' ', f.a, N' ', g.a, N' ', h.a) AS ProductName
INTO Table_with_5M_rows
FROM a, a AS b, a AS c, a AS d, a AS e, a AS f, a AS g, a AS h;"

sqlcmd を使ってデータベースに接続し、SQL スクリプトを実行して 500 万行データを持つテーブルを作成します。これは実行に数分かかるかもしれません。

sqlcmd -S <接続するSQL Serverインスタンス名> -U sa -P <saユーザーパスワード> -d SampleDB -i ./CreateSampleTable.sql

テーブルをクエリして時間を測定する Node.js アプリを作成

プロジェクトフォルダ内で、Node.js の依存関係を初期化します。

# npm パッケージをセットアップ
npm init -y
# プロジェクトフォルダに tedious と async モジュールをインストール
npm install tedious 
npm install node-uuid
npm install async 

SqlServerColumnstoreSample フォルダ内に columnstore.js というファイルを作成します。
パスワードは自分のものに置き換えることを忘れないでください。

columnstore.js
var Connection = require('tedious').Connection;
var Request = require('tedious').Request;
var uuid = require('node-uuid');
var async = require('async');

var config = {
    server: 'localhost',              // 接続先の SQL Server インスタンス
    authentication: {
        type: 'default',
        options: {
            userName: 'sa',           // 接続ユーザー名
            password: 'your_password' // 接続パスワード
        }
    },
    options: {
        database: 'SampleDB'          // 接続するデータベース(ここは変えないでください)
    }
    // Azure SQL Databaseに接続するときは、次のオプションが必要
    //options: {encrypt: true, database: 'yourDatabase'}
};


var connection = new Connection(config);
function exec(sql) {
    var timerName = "QueryTime";

    var request = new Request(sql, function(err) {
        if (err) {
            console.log(err);
        }
    });
    request.on('doneProc', function(rowCount, more, rows) {
        if(!more){
            console.timeEnd(timerName);
        }
    });
    request.on('row', function(columns) {
        columns.forEach(function(column) {
            console.log("Sum: " +  column.value);
        });
    });
        console.time(timerName);
    connection.execSql(request);
}
// 接続を開き、クエリを実行
connection.on('connect', function(err) {
    async.waterfall([
        function(){
            exec('SELECT SUM(Price) FROM Table_with_5M_rows');
        },
    ]);
});

クエリ実行にかかる時間を測定

アプリを実行し、時間を計測します。

node columnstore.js

テーブルにカラムストアインデックスを追加

SQLCMD を実行して、カラムストアインデックスを追加します。

sqlcmd -S <接続するSQL Serverインスタンス名> -U sa -P <saユーザーパスワード> -d SampleDB -Q "CREATE CLUSTERED COLUMNSTORE INDEX Columnstoreindex ON Table_with_5M_rows;"

columnstore.js スクリプトを再実行して今回のクエリが完了するまでにかかった時間に注目

node columnstore.js

おめでとうございます。カラムストアインデックスを使って Node.js アプリを高速化しました!

おわりに

以上で、「Windows上でSQL Serverを使用してNode.jsアプリを作成する」は終了です。Build an app using SQL Server には、他言語での SQL Server アプリを作成するチュートリアルがあります。ぜひ、他の言語でも試してみてください。


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

Node.jsに変わるかも?と言われている、Deno 1.0がリリースされたので素振りしてみた。

Deno1.0がリリースされたことで、twitterで界隈で話題になっていたので、素振りしてみた。(メモ程度です)

詳細は公式サイトをご確認ください。
https://deno.land/

まずは、インストール

brew install deno

これで準備はOK!まずは、「Getting Started」をやってみた。

実行コマンド: deno run (ファイル名)

deno run https://deno.land/std/examples/welcome.ts
// => Welcome to Deno ?

http://localhost:8000/にアクセスすると、「Hello World」が表示される。

hello_http.ts
import { serve } from "https://deno.land/std@0.50.0/http/server.ts";
const s = serve({ port: 8000 });
console.log("http://localhost:8000/");
for await (const req of s) {
  req.respond({ body: "Hello World\n" });
}

実行はこちら

deno run --allow-net hello_http.ts

「--allow-net」の位置が曲者で最後につけてエラーになり、位置に注意です!
ここtwiiter見てても一度ハマる人が多いようなので。
「--allow-net」はセキュリティの関係で必要みたいです。

今後もウォッチしていこう!

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