まだプログラマーですが何か?

プログラマーネタとアスリートネタ中心。たまに作成したウェブサービス関連の話も http://twitter.com/dotnsf

タグ:npm

Node.js には npm という便利なパッケージ管理ツールがあります。これはプロジェクト内に package.json というファイルを用意し、その中にプロジェクトで利用したいオープンソースライブラリを記述しておくだけで、後は
$ npm install
と実行するだけでそれらのオープンソースライブラリをまとめてインストールしてくれる、という機能があるのです。しかもその記述されたライブラリだけでなく、それらのライブラリが必要とするライブラリもまとめて調べ、まだインストールされていないものがあればそれだけを(2重にインストールしたりせずに)選別してインストールしてくれる、というものです。要するに深く考えずに npm install すれば、そのプロジェクトの実行に必要なライブラリがまとめてインストールすることができるようになります。

ただ実行環境を揃えるだけならこのように簡単なのですが、どのようなライブラリがインストールされることになり、そしてそれぞれのライブラリはどのような種類のオープンソースライセンスのもとで配布されているのか、を改めて調べようとするとちと面倒なことになります。まず上述のように導入されるライブラリは package.json に記述されたものだけではなく、それらが必要とするライブラリや、更にそれらが必要とするライブラリ・・・と、再帰的に含まれるので、小さなプロジェクトでも実際に調べる対象となるライブラリの数は膨大になってしまいます。試しに先程ウェブフレームワークとして有名な express(v4.17.1) だけを指定して npm install してみたところ、express 自身も含めて 49 個ものライブラリがインストールされました:
2019080401


なので express だけを使うプロジェクトであっても、利用するオープンソースライブラリのライセンスを調べるには、これら 49 個のライブラリそれぞれのライセンスを調べる必要がある、ということになります。

これはさすがに面倒・・・ということで、まとめて調べるツールを作ってみました:
https://github.com/dotnsf/licenses

2019080402


このツールはローカルファイルシステム内のプロジェクトフォルダ(ディレクトリ)を指定して、そのプロジェクトの中で使われるオープンソースライブラリを調べて一覧表示します。ウェブの UI をもたせることも考えたのですが、ローカルファイルシステムを指定するということは対象プロジェクトのソースコード一式をローカルシステム内に(ダウンロードなり git clone なりで)取得しているということになるので、GUI がなくても使える人が大半だろう、、、と思って付けませんでした。GUI でパイチャートとか付けたい人は改造しちゃってください。なおこのツール自体は MIT ライセンスです。

利用するにはまず上記プロジェクトをダウンロードか git clone します。実質的に licences.js だけを使います。そしてこのファイルの3行目あたりにある target_folder 変数の値を調査対象とするプロジェクトのフォルダとなるよう書き換えてください(例: "../myproject" など)。

そして Node.js で実行します:
$ node licenses

成功すると target_folder で指定したフォルダ内の node_modules/ サブフォルダの中を調べ、更に導入済みライブラリのサブフォルダを調べて各ライブラリ毎に配布ライセンスを推測します。そして全てのライブラリを調べ終わったら、結果を一覧表示します(下は例、赤字はコメント):
$ node licenses
target_folder = '../myproject'  対象フォルダ名
MIT: 73.52% MITライセンスが73.52%
[ 'accepts',  MIT ライセンスと推測されたライブラリ一覧
  'abbrev',
  'append-field',
     :
]
ISC: 21.32% ISCライセンスが21.32%
[ 'are-we-there-yet', ISCライセンスと推測されたライブラリ一覧
  'chownr',
  'console-control-strings',
    :
]
Apache: 4.41% Apacheライセンスが4.41%
[ 'detect-libc', Apacheライセンスと推測されたライブラリ一覧
  'ejs',
  'cfenv',
    :
]
BSD: 0.73% BSDライセンスが0.73%
[ 'esprima' ] BSDライセンスと推測されたライブラリ一覧

これでいちいちフォルダ毎に調べなくても依存ライブラリのライセンスがわかります。たぶん。



Node.js で MySQL データベースを利用する場合、npm の mysql ライブラリが多く使われると思っています:
mysql - npm


このライブラリを使うと、例えば SELECT 文を実行するのであれば、こんな感じに記述することで実装できます:
var Mysql = require( 'mysql' );

//. データベースへ接続
var mysql = Mysql.createConnection({
  host: '192.168.10.10',
  user: 'username',
  password: 'password',
  database: 'mydb'
});
mysql.connect();

//. SELECT 文実行
mysql.query( 'select * from items where price > 1000', function( error, results, fields ){
  if( error ) throw error;
  results.forEach( function( result ){
    var id = result.id;
       :
       :

    //. 終了
    mysql.end();
  });
});

また INSERT 文やプレースホルダーっぽい機能を使うこともできます:
var Mysql = require( 'mysql' );

//. データベースへ接続
var mysql = Mysql.createConnection({
  host: '192.168.10.10',
  user: 'username',
  password: 'password',
  database: 'mydb'
});
mysql.connect();

//. INSERT 文実行
mysql.query( 'insert into items set ?', { id: 1234, name: 'シャンプー', price: 500 }, function( error, result ){
  if( error ) throw error;
       :
       :

    //. 終了
    mysql.end();
  });
});

詳しくは上記公式ページを参照してください。


さて、MySQL では create table でテーブルを定義する際に blob(バイナリラージオブジェクト)型の列を指定することができます。画像ファイルなどバイナリデータをそのまま格納することができる列が定義できます:
> create table items( id int primary key, name varchar(50), price int, img blob );

定義は上記のように指定すればいいのですが、ではこの blob 列に、特に mysql ライブラリを使ってどのように指定すればデータを格納すればよいか、が今回のテーマです。特にウェブ画面から画像ファイルを指定してアップロードするような場合に、その画像ファイルの内容を具体的にはどのようにして blob 列に格納すればよいか、という内容です。

この要件について、少しググると MySQL の LOAD_FILE() 関数を使う方法が見つかります。この場合、具体的には以下のように記述します(目的の画像ファイルが /tmp/aaa.png に存在すると仮定します):
    :

mysql.query( 'insert into items set ?', { id: 1234, name: 'シャンプー', price: 500, img: LOAD_FILE('/tmp/aaa.png') }, function( error, result ){
: :

この方法はローカル MySQL サーバーに対しては有効に利用できます。LOAD_FILE() 関数はサーバー側のファイルシステムに対して実行されます。なので上記命令を実行してデータを格納する MySQL サーバーのファイルシステムに /tmp/aaa/png というファイルが存在していれば正しく動きます(命令を実行するクライアント側のファイルシステムにあっても動きません)。

しかし一般的なウェブシステムではウェブサーバーとデータベースサーバーは分離しています。そのようなケースでは(ウェブサーバーはデータベースクライアントになるので)ユーザーがウェブでアップロードしたファイルはウェブサーバーに一時格納されるだけで、データベースサーバーへは送られません。そこでウェブサーバー上で LOAD_FILE を実行してもデータは格納できないことになります。

では改めて、 LOAD_FILE() を使わずに blob データをどのように MySQL に格納するか、その方法がこちらです:
var fs = require( 'fs' );
    :
var img_content = fs.readFileSync( '/tmp/aaa.png' );
mysql.query( 'insert into items set ?', { id: 1234, name: 'シャンプー', price: 500, img: img_content }, function( error, result ){
    :
    :

ファイルシステムライブラリである fs をロードし、readFileSync 関数でローカルの(ウェブサーバー上の)バイナリファイルを読み込み、その結果をプレースホルダーに指定するだけです。

ちなみに取り出すときはこんな感じ:
    :
mysql.query( 'select * from items where id = 1234', function( error, results, fields ){
  if( error ) throw error;
  var img_content = results[0].img;
    :
    :

パフォーマンス等の観点からバイナリラージオブジェクトを MySQL などのデータベースに格納するべきか?という問題はあると思いますが、S3 ストレージなどの外部に格納する場合と比べて「データベースのバックアップ/リストアでオブジェクトごとバックアップ/リストアされる」というメリットはあります。用途に応じては使う価値があると思っています。



IBM Domino 10 の新機能の1つで、個人的にすごく楽しみにしていた domino-db パッケージを遅ればせながら使ってみました。

このパッケージについて簡単に説明します。IBM Domino サーバーのアプリケーションデータベースにアクセスする方法は何通りかありますが、一般的にはこれまで以下の3通りありました:

(1) 専用クライアント(IBM Notes)を使う
(2) サーバー側で HTTP タスクを有効にした上で、ブラウザなどの HTTP クライアントからアクセスする
(3) IBM Notes が導入されたシステムから C/C++ や Java、ActiveX でプログラミングを行ってアクセスする

(1) はごくごく一般的な、いわゆる「ノーツ」を使ってアクセスする方法で、(2) はブラウザを使ってアクセスします。この2つはプログラミングというよりは、普通に提供されている機能です。(3) はプログラミングによってアプリケーションやマクロを作成してアクセスする方法なのですが、そのための SDK やインターフェースは同システム内にノーツが導入されている前提で提供されているものでした。つまりプログラミングで IBM Domino サーバーにアクセスする場合、IBM Notes クライアントがインストールされていることが前提条件であり、IBM Notes がインストールされていない(またはインストールできない)環境からはプログラミングしてもネイティブコードが存在しないので実行できない、という制約があったのでした。


このたび IBM Domino 10.0 から提供された App Dev Pack という拡張機能によって、この制約がなくなりました。つまり、

(4) App Dev Pack で提供される domino-db パッケージを使ってプログラミングし、Node.js からアクセスする

という4つ目の選択肢が新たに与えられたことになります。なお App Dev Pack は IBM Domino 10.0.1 と同時に提供が開始されました。またこの段階では Linux 版の App Dev Pack のみが提供されています。詳しくは以下でも紹介しますが、2019/01/14 時点では Linux (CentOS7/RHEL7)版の IBM Domino 10.0.1 サーバーと、Linux 上の Node.js 環境で動作します(2019/01/18 修正 domino-db パッケージは Linux 以外でも動くようです。少なくとも Windows からは動きました)

この App Dev Pack を実際に使って試してみました。今回は以下のような2台の Linux システム間で実際に動かしています:
2019011302


【IBM Domino サーバー】
OS: CentOS 7
Domino: 10.0.1

【クライアント】
OS: Ubuntu 16.04
Notes: インストールせず
Node.js: 8.11

今回は同一ネットワーク上に上記2台の Linux マシンを構築していますが、実際にはクライアントから IBM Domino サーバーに(名前とポート番号指定だけで)アクセスすることが可能であれば、インターネット越しでも可能だと思っています。


【環境準備】
CentOS 7 上に IBM Domino 10.0.1 環境を構築します。こちらの手順はすぐ下に記載した部分以外は省略します(私がこちらを参照したわけではないのですが、詳しくはこちらに記載された方法で導入できるようです)。なお Linux 版の Domino 10.x は CentOS/RHEL 7 以上でないとインストールできません。

ただ上記リンク先に書かれていなくて、ちと注意が必要な点があるのでそこだけ追記しておきます。

後述の環境では(Proton の動作ポートが動的な設定のままでも動くように)インフラ部分のセキュリティレベルをかなり緩めに作りました。具体的には SELinux を無効にし、更にファイアウォール(firewalld)も止めている、という点をコメントしておきます。具体的なコマンドとしては IBM Domino インストール後に root でログインして以下を実行しました:
(firewalld を停止)
# systemctl stop firewalld

(firewalld の無効化)
# systemctl disable firewalld

(SELinux の設定ファイルを編集)
# vi /etc/selinux/config

(SELINUX=enforcing とあった行を以下のように更新して、保存)
SELINUX=disabled

(システム再起動)
# shutdown -r now

これでファイアウォールや実行ポート番号を意識せずに以下の記述内容が実行できる環境にしています。



そして上記の環境を使い、CentOS 7側には Proton というサーバータスクを、Ubuntu 側には domino-db パッケージをそれぞれ追加導入しています。つまりシステム的にはこんな感じで Proton と domino-db パッケージを経由して通信しています:
2019011303


以下、それぞれの追加モジュールについて紹介します。

まずクライアント側に domino-db という npm パッケージを用意します。このパッケージが IBM Domino とのアクセスを提供しており、Node.js を使ったプログラム内で IBM Domino のデータベースを読み書きすることができるようになります。なお 2019/01/14 時点で提供されている domino-db パッケージのバージョンは 1.1.0 であり、Linux 版のみが提供されているようでした。

またサーバー側にも Proton というアプリケーション(正確にはサーバータスク・アプリケーション)が必要です。この Proton が前述の domino-db を使ったアプリケーションからのリクエストやレスポンスに対応します。なお 2019/01/14 時点で提供されている Proton のバージョンは 0.2.2 であり、こちらも Linux 版のみが提供されているようでした。

これら2つのモジュールが互いに通信しあって Node.js プログラムからの IBM Domino へのリクエスト制御やデータベースの読み書きに対応します。


実際にこれらをインストールする手順を紹介します。まず Domino 10 のインストールモジュールをダウンロードした IBM サイトなどから IBM Domino AppDev Pack をダウンロードします。2019/01/15 時点でのバージョンは 1.0 で Linux English 版のみ、 DOMINO_APPDEV_PACK_1.0_LNX_EN.tgz というファイル名でした:
2019011502


このアーカイブファイルを展開すると、以下のようなファイル群が表れます。なお、今回以下で紹介するものの中で使っているのは青のファイルのみで、これらのファイルが /tmp 以下に展開されているものとします:
- LICENSE
- NOTICE
- domino-appdev-docs-site.tgz : ドキュメント
- domino-domino-db-1.1.0.tgz : domino-db パッケージ
- domino-iam-service-1.0.15.tgz : IAM 認証用モジュール
- domino-node-iam-client-1.0.22.tgz : IAM 認証用の Node.js クライアントモジュール
- node-demo.nsf : デモ用アプリケーション
- oauth-dsapi-0.2.2.tgz : OAuth 用モジュール
- proton-addin-0.2.2.tgz : Proton 一式

なお、以下で紹介する内容は domino-appdev-docs-site.tgz を展開したドキュメント内にかかれている内容をベースに独自にカスタマイズしたものです。以下ではこのファイルを参照することはありませんが、domino-db パッケージを使ってアプリケーション開発をする場合の API リファレンスなども含まれているので、こちらは必ず手元で参照できるようにしておきましょう:
2019011501


まずはサンプルで使うドミノデータベース node-demo.nsf をデータフォルダに移動させます。いったん IBM Domino サーバーをシャットダウンし、notes ユーザー(Linux 内で IBM Domino を実行するユーザー)でログインします。そして node-demo.nsf をデータフォルダ直下にコピーしておきます:
$ cd /local/notesdata

$ cp /tmp/node-demo.nsf .

$ chown notes.notes node-demo.nsf

なお、このデータベースに特別な設計が含まれているわけではありません。ただし、今回の設定では Anonymous ユーザーでこのデータベースにアクセスして文書を作成することになるので、Anonymous ユーザーで文書を読み書きができるような権限設定が必要です。他のデータベースを使う場合も同様です。

次に IBM Domino 10 サーバーに Proton を導入します。notes ユーザーで以下のコマンドを実行してファイルコピー&セットアップを行います:
Domino バイナリのあるフォルダに移動
$ cd /opt/ibm/domino/notes/latest/linux

libnotes.so が存在していることを確認
$ ls -la libnotes.so

Proton ファイルをこのフォルダ内に展開
$ sudo tar -xzvf /tmp/proton-addin-0.2.2.tgz

setup_proton.sh を使ってセットアップ
$ sudo sh -v ./setup_proton.sh

この時点で Proton を起動することは可能ですが、デフォルト状態のままだと同一システムからのリクエストのみ受け付けます。今回は外部の別マシンから Node.js アプリケーションを実行したいので、外部リクエストを受け付けるための設定を追加する必要があります。

そのため、データフォルダ(/local/notesdata/)以下の notes.ini ファイルをテキストエディタで開き、以下の1行を追加します:
PROTON_LISTEN_ADDRESS=0.0.0.0

PROTON_LISTEN_ADDRESS は Proton へのリクエストを受け付けるホストの IP アドレスです。デフォルトは 127.0.0.1 なので同一システムからのリクエストのみ受け付けます。上記の 0.0.0.0 は全ての外部ホストからのリクエストを受け付ける、という設定です。

ここで1つ注意を。実は Proton の実行ポート番号は動的に決まるのですが notes.ini に以下の一行を追加することで実行ポート番号を指定できることになっています(この例の場合は 6000 番ポート):
PROTON_LISTEN_PORT=6000

が、私が試した限りでは、この1行を追加すると Proton 自身が起動に失敗するようになってしまいました。原因はよくわからないのですが、先に進めるため、今回は上記の設定はしていません。つまり Proton は起動時に空きポートを探して動的なポートで起動されるようにしています。

ここまでの設定ができたら IBM Domino サーバーを起動し、更に起動後のサーバーコンソールに以下を実行して Proton サーバータスクを実行します:
> load proton

するとコマンドに続いて以下のような実行ログが表示されます:
> load proton

PROTON> Build 0.2.2
PROTON> Listening on 0.0.0.0:1217, INSECURE
PROTON> Note: Requested port was 0, Actual listen port is 1217
PROTON> Server initialized
PROTON> Server allows Anonymous access only.
  :

この場合の例では Proton は 1217 番ポートが空いていることを確認し、1217 番ポートでリクエストを待ち受けている状態になりました。この番号は後で使うので覚えておきましょう。

これで IBM Domino サーバー側の設定は終わりです。実際には通信に SSL を使ったり、IAM による認証や権限管理を行ったりする設定もできるようですが今回は試していません。興味ある方はドキュメントを参照して挑戦してみてください。


次にクライアント側である Ubuntu システムのセットアップに移ります。まず Ubuntu に Node.js を導入します。ドキュメントによると Node.js V8.x で動作確認を行っている模様なので、今回も Node.js V8 を導入することにします(V10.x でも動くらしい、とは書かれていました、念の為)。多くの環境で Node.js を導入するとデフォルトで V8.x になると思っていますが、6以下だったり10以上だったりする場合は nvm などを使って Node.js V8.x (と npm)が動く環境を用意してください。

そして AppDev Pack 内に含まれている domino-db パッケージを転送するなどして、Ubuntu 側にコピーしておきます(以下の紹介ではカレントフォルダ内に domino-domino-db-1.1.0.tgz が存在しているものとします)。これでアプリケーション開発の準備は完了です。

チュートリアルをベースに、以下のような Node.js プログラムコード(test01.js)を記述します:
//. test01.js

//. domino-db のロード
const { useServer } = require( '@domino/domino-db' );

//. Domino サーバー(192.168.1.100)と Proton の実行ポート(1217)を指定
const serverConfig = {
  hostName: '192.168.1.100',
  connection: {
    port:1217
  }
};

//. 対象とするデータベース
const databaseConfig = {
  filePath: 'node-demo.nsf'
};

//. データベースに新規に作成する文書の内容
const createOptions = {
  documents: [
    {
      Form: 'Contact',
      Firstname: 'Kei',
      LastName: 'Kimura',
      City: 'Funabashi',
      State: 'Chiba'
    }
  ]
};

//. domino-db を使って Proton に接続
useServer( serverConfig ).then( async server => {
  //. 接続に成功したら処理対象データベースを指定
  const database = await server.useDatabase( databaseConfig );

  //. 設定した内容で文書を作成
  const response = await database.bulkCreateDocuments( createOptions );

  //. 作成した文書の unid を取得
  const unids = response.documents.map( doc => doc['@unid'] );

  //. 作成した文書の unid をコンソールに表示して終了
  console.log( `Documents created: ${unids}` );
});

Node.js に慣れている人であればなんとな~く処理内容は理解できると思います。1点だけコメントすると、上記の青字部分で指定した内容でデータベース内に新規文書を作成します。上記例では documents は配列になっていますが、その要素は1つだけです(つまり1文書だけ作成します)。配列要素の中身は作成する文書の各フィールドとその値を指定します。つまり今回の例では以下のようなフィールドとその値を指定してノーツの文書を新規に作成することになります:
フィールド名フィールド値
FormContact
FirstNameKei
LastNameKimura
CityFunabashi
StateChiba


また documents は配列なので、ここに2つ以上のオブジェクトを指定することも可能です。その場合は2つの文書がバルクインサートで作成され、結果の unid も2つ得ることができます。 ただ今回は1文書のみ作成する前提で上記コードが記述されている点にご注意ください。

このコードを実行してみます。まず上記で作成した test01.js ファイルがあるフォルダで domino-db パッケージを npm install します。domino-db はまだ npmjs に登録されているわけではなく、あくまでインストールモジュールから導入する必要があります。したがって以下のようなコマンドでローカルファイルシステムの同一フォルダにある domino-domino-db-1.1.0.tgz を指定してインストールします:
$ npm install ./domino-domino-db-1.1.0.tgz --save

domino-db のインストールができれば test01.js を実行することができます。また実行結果には作成された文書の UNID が表示されます:
$ node test01

Documents created: 5F29E1B7FD62450649258383005083FE

実際に Domino 10 サーバー上の node-demo.nsf ファイルを開いてみると、All Names ビューから Name が Kei Kimura となった文書が追加されていることが確認できます:
2019011301


この文書を開いてみると、入力した通りの内容で文書が作成されていることが確認できます。本当に Notes/Domino の導入されていないシステムから domino-db パッケージを使ってデータベース内に文書を作成することができました:
2019011503


本当に domino-db パッケージ(と Node.js)だけでリモートの IBM Domino データベースに文書を作成することができちゃいました。今はサーバー側もクライアントも Linux 環境でないとできない(2019/01/18 修正 サーバー側が Linux 環境でないとできない)、という制約がありますが、いずれ他のシステムでもこの機能が提供される(勝手にそう思ってますが・・)と超便利だと思いました。

1点だけ、Proton の実行ポート番号を notes.ini で指定すると動かなくなる、という現象は自分だけなのでしょうか?この辺りはまだ情報があまりなく、うまい回避方法があるといちいち実行ポートをコンソールで確認する必要がなくなって便利なんだけどな・・・ 情報求む。


IBM Cloud から提供されている IBM CloudantApache CouchDB をベースとしたマネージドな NoSQL データベースのサービスです:
2018090300


ベース製品が同じなので、例えば REST API レベルでは互換性があります。注意が必要な点として自分が気づいた限りでは IBM Cloudant は標準で Apache Lucene ベースの検索機能が有効になっており、インデックスとなる Design Document を用意することでテキスト検索が可能になる、ということが挙げられますが、それ以外に大きな差はありません。 一方で IBM Cloud から提供されているライトアカウント(無料プラン)でも IBM Cloudant を利用することができるので、わざわざ Apache CouchDB を用意しなくても気軽に使うことができる DBaaS としてとても手軽で便利だと思っています:
2018090301


さて、自分は業務のプログラミングでは主に Node.js を使うのですが、Node.js のパッケージライブラリには IBM Cloudant 用のものと、Apache CouchDB 用のもの、両方が存在しています:
(IBM Cloudant)
2018090302

(Apache CouchDB)
2018090303


仮に対象となるデータベースが IBM Cloudant であれば前者の方が簡単に使えるという印象を持っています。ただし IBM Cloudant 用ライブラリは IBM Cloud 上の IBM Cloudant を想定していることもあり、例えばオンプレミス上の Apache CouchDB に対して使えるものではありません。

一方、Apache CouchDB 用ライブラリはローカルや社内サーバー、クラウド上にある Apache CouchDB データベース全般に対して利用することが可能です。この対象はクラウド上の IBM Cloudant であっても構いません。要するにこちらのライブラリを使えば Apache CouchDB だけでなく IBM Cloudant にも接続できる、ということです。


実際にこういった需要がどれだけあるのかわからないのですが、例えばあるシステムを作る際に、そのデータストアとして、
(1) 試しに動かす場合は IBM Cloud 上の IBM Cloudant を使って気軽に開発/テストを行い、
(2) 本番運用ではオンプレミスな Apache CouchDB を利用する(IBM Cloudant の独自機能は使わない想定)

といったことが接続先の切り替えだけでできると便利です。ただこれを実現するためには IBM Cloudant 用の便利なライブラリを使って開発しまうと (2) の本番の時に問題が起こってしまいます。以下では IBM Cloudant に対しても Apache CouchDB 用ライブラリ(以下 node-couchdb)を使ってアクセスするように実装してみたコードを紹介します。ベースが同じ製品なのでできることはできるんですが、そのための手順と注意点を含めて紹介します。


【準備】
まず Node.js のコードを記述する前に上述の node-couchdb を npm install しておきます:
$ npm install node-couchdb


【データベース接続】
node-couchdb を使って IBM Cloudant に接続します。こんな感じのコードを記述します:
var dblib = require( 'node-couchdb' );

var option = {
  auth: {
    user: 'username',
    pass: 'password'
  },
  'host': 'username.cloudant.com',
  'protocol': 'https',
  'port': 443
};
var db = dblib( option );

usernamepassword の部分にはそれぞれ IBM Cloudant の username と password を指定します(localhost の Apache CouchDB に接続する場合は option = {} で接続できます)。これで IBM Cloudant との接続ができました。ここで取得した db を使って、以下の主要な操作を行うことができます。


【主要な操作】

ドキュメント追加
insert() メソッドにデータベース名(以下の例では 'testdb')を指定して、ドキュメントを追加します。取得前に db.uniqid() でユニーク ID を取得し、_id に設定している点に注意してください:
var doc = { name: 'dotnsf', height: 170.0 };  //. 追加するドキュメント
db.uniqid().then( function( id ){
  doc._id = id[0];
  db.insert( 'testdb', doc ).then( function( body, headers, status ){
    console.log( body );
  }).catch( function( err ){
    console.log( err );
  });
});

ドキュメント読み取り
同様に get() メソッドにデータベース名と id を指定してドキュメントを取得します:
db.get( 'testdb', id ).then( function( doc, headers, status ){
  console.log( doc );
}).catch( function( err ){
  console.log( err );
});

ドキュメント削除
del() メソッドにデータベース名と id と rev を指定して、データベースからドキュメントを削除します。以下の例では一度 get() メソッドを実行して id 値から rev 値を取り出してから del() を実行しています :
db.get( 'testdb', id ).then( function( doc, headers, status ){
  db.del( 'testdb', doc.data._rev ).then( function( data, headers, status ){
    console.log( data );
  }).catch( function( err ){
    console.log( err );
  });
}).catch( function( err ){
  console.log( err );
});

ビューを指定してドキュメント一覧取得
あらかじめ作成したビューを指定して、そのビューに含まれるドキュメントの一覧を取得します。以下の例ではデザイン名 : library, ビュー名 : byname というビューを指定して文書一覧を取得しています :
db.get( 'testdb', '_design/library/_view/byname', {} ).then( function( data, headers, status ){
  if( data && data.data ){
    var docs = data.data.rows;
    console.log( docs );
  }
}).catch( function( err ){
  console.log( err );
});


IBM Cloudant の npm だと最初にデータベース名を指定してそのデータベースのオブジェクトを取得した上で各種操作を行う、、という流れなんですが、Apache CouchDB 版だと毎回データベース名と一緒に各種操作を行う、、という点が大きな違いだと思いました。ただその辺りさえ理解していればまあ大丈夫かな。。


 

これまであまり積極的に使うことのなかった webpack を使ってみました:
2018082300


webpack は複数のモジュール(ファイル)を1つに(場合によっては複数に)まとめるバンドラ(bundler)ツールです。Node.js でいうと、エントリーポイントとなる main.js ファイルと、ここから呼ばれるサブ機能が sub1.js や sub2.js などの別ファイルに分かれている場合に、これら3つのファイルを1つのファイルにまとめるというものです。

【サンプルの紹介】
今回は jsonwebtoken パッケージを使ったシンプルなサンプルコード(main.js)を用意しました:
// main.js
const jwt = require( 'jsonwebtoken' );

function main( params ){
  const id = params.id;
  const password = params.password;
  const token = jwt.sign( { id: id, password: password }, 'secret' );

  console.log( token );
}

main({
  id: 'userid',
  password: 'password'
});

global.main = main;

↑ main() 関数の中で2つのパラメータ(id と password)を受け取り、JSON をトークン化して出力する、というものです。そして id = 'userid', password = 'password' を指定して main を実行しています。

この main.js を実行する前に、コード内で使っている jsonwebtoken パッケージをインストールする必要があります。以下の内容の package.json を用意します:
{
  "name": "jwt_test",
  "version": "0.0.1",
  "main": "main.js",
  "dependencies": {
    "jsonwebtoken": "^8.3.0"
  }
}

そして以下のコマンドで jsonwebtoken パッケージをインストールします:
$ npm install


その後に main.js を普通に実行すると、{ id: 'userid', password: 'password' } をトークン化した結果が表示されます:
$ node main.js
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6ImRvdG5zZiIsInBhc3N3b3JkIjoicGFzc3dvcmQiLCJpYXQiOjE1MzQ5OTU5NTh9.LUx0q5ycwIFeR1HNnNFgUrlX0w0cny0qDVsPmiVtVnI

とりあえずこの状態で動くことは確認できました。コードは main.js 1つだけに記述されていますが、jsonwebtoken パッケージを利用しているので、実行前に $npm install (jsonwebtoken) を実施しておく必要があり、実際にはここでインストールされるモジュールファイルも使われながら動作します。これらを1ファイルにまとめた上で実行できるようにするというのが今回の目的です。


【webpack のインストール】
webpack は npm を使ってインストールします。例えば以下のコマンドでグローバルインストールすることができます:
$ npm install webpack -g

今回は webpack を(このプロジェクト用に)ローカルインストールして使うことにします。なお webpack 4.0 以降では webpack-cli も合わせて導入する必要があるのでまとめてインストールし、webpack コマンドが実行できるようローカルモジュールへのパスを通します:
$ npm install webpack webpack-cli

$ export PATH=$PATH:./node_modules/.bin


【config ファイルの用意】
webpack を使ってバンドルする作業の条件を config ファイルと呼ばれるファイルで指定します。以下の内容で webpack.js ファイルを作成します:
var path = require( 'path' );
module.exports = {
  entry: './main.js',
  output: {
    path: path.resolve( __dirname, 'dist' ),
    filename: 'bundle.js'
  },
  target: 'node'
};

↑ main.js をエントリーポイント(最初に実行するファイル)とする一連の処理の中で必要とされるモジュール(今回の例では jsonwebtoken パッケージ)がバンドルの対象となり、バンドル結果は dist/bundle.js というファイルに出力される、という指定をしています。


【バンドルと実行】
ここまでに準備した環境とファイルを使って webpack を使ってみます。

まずはバンドル、上記で作成した webpack.js を config ファイルとして指定して webpack コマンドを実行します:
$ webpack --config webpack.js

バンドルが成功すると dist/bundle.js というファイルが生成され、この1ファイルに main.js と jsonwebtoken パッケージがバンドルされているはずです。

確認のため生成されたファイルを実行します。念の為、これらのパッケージモジュールが存在しないテンポラリディレクトリ(/tmp)に bundle.js ファイル1つだけをコピーした上で実行します:
$ cp dist/bundle.js /tmp
$ cd /tmp
$ node bundle.js
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6ImRvdG5zZiIsInBhc3N3b3JkIjoicGFzc3dvcmQiLCJpYXQiOjE1MzUwMDM1MjF9.llZuuWxqX607AWrJBvMEDpFksFX8IvigUrdIWbhItRg

期待通りにトークンが表示される結果になりました。この bundle.js 1ファイルの中に jsonwebtoken パッケージやその依存パッケージまでが含まれる形でバンドルされて実行されたことが確認できました。


webpack を使うことで今回の例のように依存関係ごとファイルをまとめることができるだけでなく、ファイルのモジュール化が促進されたり、一度のロードで全ファイルを読み込めることから SPA(Single Page Application) が作りやすくなったりします。その一方で、バンドル先のファイルが大きくなってしまうと最初のロードに時間がかかるようになる、という問題もあります。個人的にはパッケージ化されたあとに開発コンソールを使ったデバッグが難しくなることに懸念も持っています。

まあケース・バイ・ケース、なんだろうなあ。。

 

このページのトップヘ