NEM NanoWalletのUtilsをnpmモジュール化する

自分はNEMという暗号通貨が好きで、Javascriptからアドレスを生成したり、トランザクションを発行したりなど色々触って遊んでいました。

tadajam.katari.be

NEMはもともとAPIが提供されているので、たいしたスキルを必要とせずにAPIにアクセスすることで簡単に送金が出来たり、素人が何か開発しようとするには非常に敷居の低い暗号通貨です。

その流れでAngular2アプリの開発に興味を持つようになり、Node.jsを調べる内にバックエンドも含めてJavaScriptでなんでも出来てしまうということを知り、今はその辺の勉強も兼ねてNEMのモザイクの交換所でも作ろうかと思っています。

そこで、初めの一歩として、NEMのアドレスを生成したり、トランザクションを発行したり、署名を検証したりする機能を、NanoWalletから移植していきます。

NanoWallet

まずはNanoWalletの構成について整理します。

NanoWalletはNEMのAPIにアクセスすることで、自らサーバを立てることなくNEMのほとんどの機能を使えてしまう優れたアプリケーションです。 AngularJSで作成されていて、実際にトランザクションを発行したり、APIに問い合わせたりする機能の実装は、以下のservices配下にあります。

github.com

今回はその一歩手前で、各種サービスから利用されている以下utils配下にある機能を、今後自前で開発するサービスなどから簡単に呼び出せるようにしていきます。

github.com

今まで.angular-cli.jsonscriptsでそれぞれ読み込んで使っていたのですが、手順をバックエンド側と共通化したいのでnpmモジュール化してみます。

最終的なディレクトリ構成は以下のようになります。

nem-utils
│  index.js
│  LICENSE
│  package.json
│  README.md
│
└─lib
        Address.js
        bip32.js
        convert.js
        CryptoHelpers.js
        helpers.js
        KeyPair.js
        nacl-fast.js
        Network.js
        nodes.js
        nty.js
        Serialization.js
        sinks.js
        TransactionTypes.js

リポジトリの作成

せっかくなのでGitHubリポジトリを作成しておきます。

git initで初期化し、git remote add originで事前に作成しておいたリモートリポジトリのアドレスを登録。 git pullで自動生成されたLICENSEとREADME.mdを拾ってきます。

$ mkdir nem-utils
$ cd nem-utils
$ git init
$ git remote add origin https://github.com/tadajamdev/nem-utils.git
$ git pull origin master

LICENSEとREADME.mdの中身は適当に作成します。

モジュール作成

ユーティリティ側は極力もとのコードを触らずに、また呼び出し方も同じように呼べるようにという考え方で進めます。

初めに、モジュールの設定をpackage.jsonに登録します。

package.json

{
  "name": "nem-utils",
  "version": "0.0.1",
  "description": "NEM's NanoWallet Utils",
  "main": "./index.js",
  "author": {
    "name": "tadajam",
    "email": "tadajam.dev@gmail.com"
  },
  "homepage": "https://github.com/tadajamdev/nem-utils",
  "license": "MIT",
  "keywords": [
    "nem"
  ],
  "dependencies": {
    "crypto-js": "^3.1.9-1"
  }
}

CryptoJSはNanoWalletのソースから拾うのではなく、普通にnpmでインストールします。

npm install --save crypto-js

lib配下に各jsファイルを格納して、index.jsですべてのファイルを読み込みます。 exprtsやらmodule.exportsやらイマイチ理解していないのですが、以下のようにすれば一応動きます。

index.js

exports.Address = require('./lib/Address');
exports.bip32 = require('./lib/bip32');
exports.convert = require('./lib/convert');
exports.CryptoHelpers = require('./lib/CryptoHelpers');
exports.helpers = require('./lib/helpers');
exports.KeyPair = require('./lib/KeyPair');
exports.nacl = require('./lib/nacl-fast');
exports.Network = require('./lib/Network');
exports.nodes = require('./lib/nodes');
exports.nty = require('./lib/nty');
exports.Serialization = require('./lib/Serialization');
exports.sinks = require('./lib/sinks');
exports.TransactionTypes = require('./lib/TransactionTypes');

また、このままだとエラーが出たので、各ユーティリティファイル側を少し修正します。

各ファイルのimportのところで怒られるので、requireで読み込みます。 また、併せてCryptoJSが必要なファイルでは、同じようにCryptoJSも読み込みます。

/** @module utils/Address */

// import convert from './convert';
// import Network from './Network';

let CryptoJS = require("crypto-js");
let Network = require("./Network");
let convert = require("./convert");

GitHubに公開

git addでインデックスに追加、git commitでインデックスに追加されたファイルをコミットし、git push origin masterでリモートリポジトリに反映させます。

$ git add .
$ git commit -m "[add]init"
$ git push origin master

公開されました。 github.com

npmモジュール公開

npmの公式ページから開発者として登録をします。 www.npmjs.com

以下項目を入力し、登録しました。

  • Name
  • Public Email
  • Username
  • Password

npm adduserでログインし、npm publishで公開します。

$ npm adduser
Username: tadajam
Password:
Email: (this IS public) tadajam.dev@gmail.com
Logged in as tadajam on https://registry.npmjs.org/.
$ npm publish

公開されました。 www.npmjs.com

モジュールの利用

Angularのコンポーネントから使用してみます。

まずはインストールします。

$ npm install --save nem-utils

インポートして使用します。

app.component.ts

import { Address, KeyPair, Network } from "nem-utils";

console.log(Address.toAddress(KeyPair.create("").publicKey.toString(), Network.char2Id("N")));

秘密鍵を空文字列で生成した場合のアドレス、NBONKWCOWBZYZB2I5JD3LSDBQVBYHB757WJ2KDEBが出力されました。

バックエンド側でも同じようにインポートすれば、同じように使えます。  

 

 


※筆者のモチベーション向上のため、以下NEMアドレスへxemなりシットトークンなりの寄付を受け付けています。

NDY4RH-UZ3CZO-Z53O5H-NEXTEM-7UF5X3-MMDGH4-IMAD

MEANスタック入門(6) MongoDBとの連携

今回はMongoDBのシェルからデータベースを操作する方法と、Node.jsからアクセスする方法についてまとめます。

mongoの使い方とデータベースの作成

ここからはmongoコマンドを使って、MongoDBのシェルで作業していきます。 mongoコマンドを実行すると、MongoDBのシェルに入ります。

$ mongo
データベースの準備

初めにデータベースを作成します。

useコマンドでデータベース名を指定すると、カレントが指定したデータベースに変更になります。 この段階ではsample_dbはまだ追加されず、コレクションやドキュメントを追加した段階で追加されます。

> use sample_db
switched to db sample_db

次に通常のRDBのテーブルにあたる、コレクションを作成します。

> db.createCollection("users")
{ "ok" : 1 }

show collectionsコマンドで実際に作成出来たか確認します。

> show collections
users

最後に、通常のRDBのレコードにあたる、ドキュメントを追加します。

usersコレクションに対して作業するので、dbに続けてdb.usersのようにコレクション名を指定します。 コレクションが存在しなければ、コレクションを新規で作成した上でドキュメントが追加されます。

> db.users.save({id:0, address: "address0"})
WriteResult({ "nInserted" : 1 })

findで実際にドキュメントが追加されたことを確認します。

> db.users.find()
{ "_id" : ObjectId("590dbde126111dfac549518e"), "id" : 0, "address" : "address0" }
(参考)データベースの削除

removeでドキュメントを全削除します。

> db.users.remove({})
WriteResult({ "nRemoved" : 1 })

dropでコレクションごと削除します。

> db.users.drop()
true

dropDatabaseでデータベースごと削除します。

> db.dropDatabase()
{ "dropped" : "sample_db", "ok" : 1 }

show dbsで確認すると、データベース一覧から消えています。

> show dbs
admin      0.000GB
local      0.000GB
ダミーデータの準備

MongoDBのシェルではJavaScriptの構文が使えるので、以下のようなコードを作成してシェルに貼り付けると、まとめてダミーデータが作成出来ます。

use sample_db;
for(let i = 0;i < 20; i++) {
  db.users.save({id:i, address: "address" + i});
}

findで確認します。

> db.users.find()
{ "_id" : ObjectId("590dddf0f62e1b1cfe7bf271"), "id" : 0, "address" : "address0" }
{ "_id" : ObjectId("590dddf0f62e1b1cfe7bf272"), "id" : 1, "address" : "address1" }
{ "_id" : ObjectId("590dddf0f62e1b1cfe7bf273"), "id" : 2, "address" : "address2" }
{ "_id" : ObjectId("590dddf0f62e1b1cfe7bf274"), "id" : 3, "address" : "address3" }
{ "_id" : ObjectId("590dddf0f62e1b1cfe7bf275"), "id" : 4, "address" : "address4" }
{ "_id" : ObjectId("590dddf0f62e1b1cfe7bf276"), "id" : 5, "address" : "address5" }
{ "_id" : ObjectId("590dddf0f62e1b1cfe7bf277"), "id" : 6, "address" : "address6" }
{ "_id" : ObjectId("590dddf0f62e1b1cfe7bf278"), "id" : 7, "address" : "address7" }
{ "_id" : ObjectId("590dddf0f62e1b1cfe7bf279"), "id" : 8, "address" : "address8" }
{ "_id" : ObjectId("590dddf0f62e1b1cfe7bf27a"), "id" : 9, "address" : "address9" }
{ "_id" : ObjectId("590dddf0f62e1b1cfe7bf27b"), "id" : 10, "address" : "address10" }
{ "_id" : ObjectId("590dddf0f62e1b1cfe7bf27c"), "id" : 11, "address" : "address11" }
{ "_id" : ObjectId("590dddf0f62e1b1cfe7bf27d"), "id" : 12, "address" : "address12" }
{ "_id" : ObjectId("590dddf0f62e1b1cfe7bf27e"), "id" : 13, "address" : "address13" }
{ "_id" : ObjectId("590dddf0f62e1b1cfe7bf27f"), "id" : 14, "address" : "address14" }
{ "_id" : ObjectId("590dddf0f62e1b1cfe7bf280"), "id" : 15, "address" : "address15" }
{ "_id" : ObjectId("590dddf0f62e1b1cfe7bf281"), "id" : 16, "address" : "address16" }
{ "_id" : ObjectId("590dddf0f62e1b1cfe7bf282"), "id" : 17, "address" : "address17" }
{ "_id" : ObjectId("590dddf0f62e1b1cfe7bf283"), "id" : 18, "address" : "address18" }
{ "_id" : ObjectId("590dddf0f62e1b1cfe7bf284"), "id" : 19, "address" : "address19" }

Node.jsとMongoDBの連携

今回はMongooseという、Node.jsからMongoDBを操作するためのライブラリを利用します。

まずはMongooseをインストールします。

$ npm install --save mongoose

ここからはこのMongoose経由でMongoDBにアクセスしていきます。

初めにserver直下にmodelsディレクトリを作成し、usersコレクション用のusers.model.tsを作成します。 ここに以下のようにスキーマを定義し、スキーマからモデルをコンパイルします。

users.model.ts

import * as mongoose from "mongoose";

const usersModel: mongoose.Model<mongoose.Document> = mongoose.model(
  "users",
  new mongoose.Schema({
      id : {
        type: Number
      },
      address : {
        type : String
      }
  })
);

export { usersModel };

次にMongoDBへの接続について、アプリケーションのエントリポイントであるwww.tsに追記します。

listenに成功したコールバックの中で、mongoose.connectを利用してMongoDBに接続します。 接続先はサーバのポート設定を記述したconfig.tsに外部化しておきます。

www.ts

import * as http from "http";
import { app } from "../app";
import { serverPort, mongoUri } from "../config";
import * as mongoose from "mongoose";

const port = process.env.PORT || serverPort;
app.set("port", port);

const server = http.createServer(app);

server.listen(port, () => {
  mongoose.connect(mongoUri);
});

config.ts

export const serverPort: number = 4300;
export const mongoUri: string = "mongodb://localhost/sample_db";

apiの実体であるusers.tsから実際にMongoDBへアクセスします。

先ほど作成したモデルからfindし、コレクションの全ドキュメントを取得して返します。

users.ts

import { Request, Response, Router } from "express";
import { usersModel } from "../models/users.model";

const usersRouter: Router = Router();

usersRouter.get("/", (request: Request, response: Response) => {
  usersModel.find({}, function(err, users) {
    if (err) throw err;
    response.json({users: users});
  });
});

export { usersRouter };

動作確認

これで以下のような一連の流れが完成しました。

  • Angularで構築したHTMLからExpressで構築したAPIにアクセス。
  • API側ではMongoDBのデータを取得して返答する。
  • レスポンスをAngular側で整形してテーブルを表示する。

f:id:tadajam:20170506235823p:plain

おしまい

これで全6回に渡るMEANスタック入門は完了です。 ここからさらにAngular側を掘っていくとか、Express側でRESTfulなAPIを構築していくだとか、色々とやりたいことはありますので、気が向いたらまた書きたいと思います。

最後に、記事に誤っている部分を発見した方や、もっと良い方法をご存じの方はぜひお声がけ下さい。

MEANスタック入門(5) タスクランナーの代わりにnpm-scriptsを使う

ビルドから実行までを自動化するのに、gulpやgruntなどのタスクランナー代わりにnpm-scriptsを使います。

npm-scriptsの設定

開発時

開発時はそれぞれコードの変更を監視して、自動でトランスパイルを走らせます。

Angularをng serverで実行し、Express側のトランスパイルをtscで実行します。

また、変更を検知して自動でコンパイルするために別途tsc-wオプション付きで実行し、それによりソースが書き換わった際に自動で再起動するnodemonを実行します。

コマンドは順を追うと以下の通りです。

$ ng server --proxy-config proxy.conf.json
$ tsc -p ./server
$ tsc -w -p ./server
$ nodemon dist/server/bin/www.js

上記をpackage.jsonscriptsに設定し、npm-run-allを使って並列実行します。

必要なnpm-run-allnodemonをインストールしておきます。

$ npm install --save-dev npm-run-all
$ npm install --save-dev nodemon

サーバ開始前にTypeScriptのトランスパイルを実行させたいので、run-sを使って直列につなぎます。

次にそれぞれのサーバ開始と監視開始をrun-pを使って並列に実行します。 Angular側の作業順序はng serverコマンドに任せます。

また、ついでにMongoDBの起動もここでやっておきます。

package.json

"scripts": {
    "start": "run-s tsc start:p",
    "tsc": "tsc -p ./server",
    "start:p": "run-p start:mongo watch:*",
    "start:mongo": "mongod --config \"C:/MongoDB/Server/3.4/bin/mongodb.config\"",
    "watch:ng": "ng server --proxy-config proxy.conf.json",
    "watch:tsc": "tsc -w -p ./server",
    "watch:nodemon": "nodemon dist/server/bin/www.js"
}

npm runで実行すると、上記手順が一度に実行されます。

$ npm run start
公開用

それぞれ単純にビルドします。

package.json

"scripts": {
    "build": "run-p tsc build:ng",
    "build:ng": "ng build --prod"
}

同じくnpm runで実行します。

$ npm run build

これだけでもかなり効率化されました。

MEANスタック入門(4) AngularとExpressの連携

今回はExpressで作成したAPIをAngularから実際に利用しつつ、Angularの基本的な開発方法に触れていきます。

project-name/src/app配下で作業し、作業後の構成は以下のようになります。

app
│  app-routing.module.ts
│  app.component.html
│  app.component.scss
│  app.component.spec.ts
│  app.component.ts
│  app.module.ts
│
├─components
│  └─call-express-api
│          call-express-api.component.html
│          call-express-api.component.scss
│          call-express-api.component.spec.ts
│          call-express-api.component.ts
│
└─services
        call-express-api.service.spec.ts
        call-express-api.service.ts

Serviceの作成

Angularでは、各種APIへのアクセスはServiceから行います。

servicesディレクトリを作成し、Angular CLIng g serviceコマンドで必要ファイルを生成します。 ng g serviceコマンドはカレントディレクトリに依存するので、servicesディレクトリまで移動してから実行します。

$ ng g service callExpressApi

実行すると以下の2ファイルが生成されます。

  • call-express-api.service.ts
  • call-express-api.service.spec.ts

specのほうはテスト用なので、call-express-api.service.tsのほうにAPIにアクセスするgetUsersメソッドを実装します。

まず、HTTPアクセスに必要なHttpクラスをインポートしてコンストラクタの引数に指定します。 また、Http.getはObservable型の値を返すので、Observable.mapを使うためにrxjs/add/operator/mapをインポートします。

call-express-api.service.ts

import { Injectable } from '@angular/core';
import { Http } from '@angular/http';
import 'rxjs/add/operator/map';

@Injectable()
export class CallExpressApiService {

  constructor(
    private http: Http
  ) { }

  getUsers() {
    return this.http.get("api/users")
            .map(response => response.json());
  }
}

上記ではapi/usersにアクセスしていますが、ng serverで実行している場合、Angularの簡易サーバとAPIサーバが別のポートで動いているので、Angular側からAPIを呼ぶと404のエラーになります。 また、ポートまで指定してAPIにアクセスした場合も、クロスオリジンのエラーで同じく実行出来ません。

これを回避するには、プロジェクトのルートディレクトリにproxy.conf.jsonを作成し、ng server実行時に--proxy-configオプションで読み込ませます。

proxy.conf.json

{
  "/api": {
    "target": "http://localhost:4300",
    "secure": false
  }
}
$ ng server --proxy-config proxy.conf.json

これでポートの差異を意識せず開発出来るようになります。

Componentの作成

Componentはビューの部品のようなもので、各部品毎の表示や動作を定義します。

componentsディレクトリを作成し、Angular CLIng g componentコマンドで必要ファイルを生成します。

ng g componentコマンドはng g serviceと同じくカレントディレクトリに依存するので、componentsディレクトリまで移動してから実行します。

$ ng g component callExpressApi

実行するとcall-express-apiディレクトリが生成され、その配下に以下4ファイルが生成されます。

  • call-express-api.component.html
  • call-express-api.component.scss
  • call-express-api.component.ts
  • call-express-api.component.spec.ts
call-express-api.component.ts

CallExpressApiComponentクラスを作成し、先ほど作成したCallExpressApiServiceを実行して、HTML表示用の変数に代入します。

CallExpressApiServiceをインポートしてコンストラクタの引数に指定し、Serviceのインスタンスを生成するためにprovidersで読み込みます。これは、app.module.tsでグローバルに読み込んでも動きますが、今回はこのComponentだけなのでここで読み込みます。

getUsersはObservableを返すので、subscribeメソッドでデータを受け取って変数usersに代入します。

call-express-api.component.ts

import { Component, OnInit } from '@angular/core';
import { CallExpressApiService } from '../../services/call-express-api.service';

@Component({
  selector: 'app-call-express-api',
  templateUrl: './call-express-api.component.html',
  styleUrls: ['./call-express-api.component.scss'],
  providers: [CallExpressApiService]
})
export class CallExpressApiComponent implements OnInit {

  users: any;

  constructor(
    private callExpressApiService: CallExpressApiService
  ) { }

  ngOnInit() {
    this.callExpressApiService.getUsers().subscribe(u => {
      this.users = u.users;
    });
  }
}
call-express-api.component.html

APIからの返答が代入された変数usersを利用して、内容を表示するHTMLを作成します。

配列を受けて要素の数だけ繰り返し表示させる場合、ngForディレクティブを利用します。*プレフィックスを付与することでtr要素がテンプレート化され、そのテンプレートを元にusersの各要素の内容が表示されます。

call-express-api.component.html

<table border="1">
  <tr *ngFor="let user of users">
    <td>{{user.id}}</td>
    <td>{{user.address}}</td>
  </tr>
</table>

ルーティングの設定

初めに表示されるComponentはapp直下にあるapp.component.htmlです。 ここにナビゲーションを用意し、ナビゲーションをクリックするとヘッダーを残してコンテンツ部分が切り替わるような、いわゆるシングルページアプリケーションの構成にします。

routerLinkディレクティブでURIを指定し、<router-outlet>タグでコンテンツ表示位置を設定します。

app.component.html

<header>
  <h1 class="logo"><a href="/">MEAN</a></h1>
  <nav>
    <ul>
      <li><a routerLink="/users">Users</a></li>
    </ul>
  </nav>
</header>
<router-outlet></router-outlet>

Angularアプリをng newで初めに生成する際、--routing=trueを付けているとapp直下にapp-routing.module.tsが生成され、app.module.tsで既に読み込まれています。

このapp-routing.module.tsに、ルーティングさせたいURIとComponentを記述します。

app-routing.module.ts

import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';

import { CallExpressApiComponent } from './components/call-express-api/call-express-api.component';

const routes: Routes = [
  {path: 'users', component: CallExpressApiComponent},
];

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule]
})
export class AppRoutingModule { }

動作確認

ng serverで実行している場合のAngular側のテストは、http://localhost:4200にアクセスします。 f:id:tadajam:20170504094858p:plain

Usersのリンクをクリックすると、APIのレスポンスを受けてテーブルが表示されます。 f:id:tadajam:20170504095029p:plain

ビルド後はdistディレクトリのclient配下のファイルも揃うので、dist/server/bin/www.jsを実行するだけでhttp://localhost:4300からclient配下のファイルも提供されます。

MEANスタック入門(3) Expressを利用したバックエンド開発

今回はAngular CLIで作成したAngularのディレクトリ階層を足場にして、Expressを活用したバックエンドの開発を行います。
言語はTypeScriptで書いていきますので、トランスパイルの方法などにも触れていきます。

TypeScriptのインストール

基本的にコードはTypescriptで書くのでtypescriptをインストールします。
--save-devオプションを指定すると、package.jsondevDependenciesに書き込まれます。

npm install --save-dev typescript

ローカルインストールしたのでtscnode_modules/.bin/配下にあります。
node_modules/.bin/にパスを通しておくか、以下のように実行します。

$ node_modules/.bin/tsc

ここから先はパスを通した前提で進めます。

Expressのインストールとバックエンドの実装

まずはexpressとリクエストデータを処理するためのbody-parserをインストールします。 --saveオプションを指定すると、package.jsondependenciesに書き込まれます。

$ npm install --save express body-parser

ルートディレクトリにserverディレクトリを作成し、以下のような構成にします。

server
│  app.ts
│  config.ts
│  tsconfig.json
│
├─bin
│    www.ts
│
└─routes
     users.ts

エントリポイントはbin配下のwww.tsです。 server直下のapp.tsconfig.tsを参照します。

www.ts

import * as http from "http";
import { app } from "../app";
import { serverPort } from "../config";

const port = process.env.PORT || serverPort;
app.set("port", port);

const server = http.createServer(app);

server.listen(port);

設定ファイルconfig.tsにポート番号を記載します。

config.ts

export const serverPort = 4300;

www.tsの中でhttp.createServerに渡しているapp.tsには、サーバーでリクエストを受け取った時の処理を書きます。

ルーティングはここで実行し、実際の処理はroutes配下のファイルに振ります。

ちなみに開発中にAngularをng serverで実行している場合はdist/clientは空なので、ルートにアクセスしても404になります。

app.ts

import * as express from "express";
import * as path from "path";
import { json, urlencoded } from "body-parser";
import * as compression from "compression";

import { usersRouter } from "./routes/users";

const app: express.Application = express();
app.disable("x-powered-by");

app.use(json());
app.use(compression());
app.use(urlencoded({ extended: true }));

app.use(express.static(path.join(__dirname, "../client")));

app.use("/api/users", usersRouter);

app.get("/", (req, res) => {
  res.sendFile(path.join(__dirname, '../client/index.html'));
});

export { app };

users.tsを作成してapiの実体を書きます。 ここでは/api/usersにGETでアクセスがあった場合、ダミーデータのjsonを返すようにします。

users.ts

import { Request, Response, Router } from "express";

const usersRouter: Router = Router();

let users = {users: [
  {
    id: 0,
    address: "NA6IF2OMBSTK2NEMXMYDQYXT32IGPKHSMULWLAKE"
  },
  {
    id: 1,
    address: "NDSNEM2HC4IEPQXGVDOWYMJ2MIPENUXN6HFJDHYZ"
  }
]};

usersRouter.get("/", (request: Request, response: Response) => {
  response.json(users);
});

export { usersRouter };

トランスパイルとサーバーの実行

これでロジックは完成なので、トランスパイルの設定をserver直下のtsconfig.jsonに書きます。 outDirで出力先を設定します。

tsconfig.json

{
  "compilerOptions": {
    "declaration": false,
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true,
    "lib": ["es6", "dom"],
    "module": "commonjs",
    "moduleResolution": "node",
    "outDir": "../dist/server",
    "target": "es6",
    "typeRoots": [
      "../node_modules/@types"
    ]
  }
}

tsc-pオプションで上記で作成したtsconfig.jsonの位置を指定して実行します。

$ tsc -p ./server

トランスパイルされたwww.jsを実行すると、config.tsに設定したポートでサービスが提供されます。

$ node dist/server/bin/www.js

動作確認

http://localhost:4300/api/usersにアクセスすると、apiからレスポンスが返ってきます。 f:id:tadajam:20170503233422p:plain

これでExpress側の準備は一旦完了です。

MEANスタック入門(2) Angularを利用したフロントエンド開発

今回はAngular CLIを利用したAngularのHello World的な内容になります。

Angular CLIのインストール

Angular2アプリの開発には、必須という訳ではありませんがAngular CLIを利用するのがおすすめです。
ディレクトリ構成を自動的に整えてくれますし、各種Componentのひな型の生成、簡易サーバによる高速なプレビューなど、非常に効率的に開発出来ます。

今後も基本的にAngular CLIで生成されたディレクトリを足場にして以降の開発を進めていきます。

まずはangular-cliをインストールします。

$ npm install -g @angular/cli
$ ng -v
@angular/cli: 1.0.1
node: 6.9.4
os: win32 x64

Angular2アプリの作成と実行

次にAngular2アプリを作成します。

CSSではなくSCSSなどの言語を利用したい場合は--styleオプションで切り替え、ルーティングモジュール付きで生成する場合は--routingオプションで設定します。 ルーティングモジュールはapp.moduleに直接書くケースもあるようですが、別ファイルに分けたほうが後々開発しやすいと思うので、trueで実行します。

$ ng new project-name --style=scss --routing=true

作成したアプリをng serverコマンドで実行します。

$ cd project-name
$ ng server

実行するとプロジェクトがビルドされ、簡易サーバが立ち上がります。

動作確認

http://localhost:4200にアクセスすると、動作が確認出来ます。 f:id:tadajam:20170430232920p:plain

同時にソースを監視するようになっているので、コードを書き換えるたびに自動的にリビルドされて非常に便利です。

Angular2アプリのビルド

実行中のアプリを本番環境用にビルドします。

デフォルトだと、ビルドされたファイルはdistディレクトリに配置されます。 後ほどdistディレクトリをサーバ側と共有する予定なので、.angular-cli.jsonappsにあるoutDirを書き換えて出力先を変更します。

.angular-cli.json

"outDir": "dist/client"

ng buildでビルドします。 --prodオプションを付けると公開用にminifyされたファイルが生成されます。

$ ng build --prod

これでAngular2側の準備は一旦完了です。

MEANスタック入門(1) MEANスタックとは

Angularにハマってしばらく色々と実験していたものの、フロントエンド開発だけではやれないことが出てきたので、バックエンド開発も含めて一通り触ってみようと思い立ちました。

当然のようにMEANスタックに辿り着き、関連する様々な記事を読んでみましたが、イマイチ最新かつ全体を網羅した良い感じの記事に出会えませんでした。 例えば、Angularが古いAngularJSであったり、Angular CLIを使っていなかったり、また、TypeScriptではなかったり。

今更AngularJSは使いたくありませんし、Angular CLIを使いたいので、そうなるとフロントエンドもバックエンドもTypeScriptで統一したいです。ただ、資料がないということは、もしかしたら主流の構成じゃないのかもしれませんが。。

結構サイトによってフォルダ構造などもまちまちで、何が本当に最適な手法かは分からなかったのですが、GitHubなどを見ながら正解を探し求め、最終的にとりあえず辿り着いたそれっぽい結論を、自分用の備忘録も兼ねてまとめておきます。

前提知識

筆者の事前知識は以下のような状況なので、ほぼ入門者と変わらないと思われます。

  • JavaScriptは割と昔から触ったことがあった。
  • TypeScriptや、いわゆるモダンJavaScriptには最近出会った。
  • 最近AngularJSを初めて触り、その流れでAngular2を調べ始めた。
  • Angularに触るまでNode.jsは触ったことがなかった。
  • ExpressやMongoDBは今回初めて触った。

よって、とりあえず動かしてみるというレベルであれば、前提となる知識はJavaScriptを理解している程度で良いかと思います。

MEANスタックとは

MEANスタックとは、MongoDB、Express、Angular、Node.jsの頭文字をとったもので、4つを組み合わせてバックエンドからフロントエンドまで全部JavaScriptで開発してしまおうというものです。

複数の言語を行き来しなくて良いというのはかなり魅力的です。

MongoDB

JSONJavaScriptと親和性が高い、NoSQL型ドキュメント指向データベース。

Express

Node.js上で動作するWEBアプリケーションのフレームワーク。 MEANスタックで利用される場合には、主にAPIの実装などバックエンド開発を担う。

Angular

フロントエンドのフレームワーク。モダンJavaScriptの教科書とも言われ、数年ぶりにJavaScriptに触るというような人にはもってこいの教材。

Node.js

サーバーサイドJavaScriptの実行環境。

事前準備

Node.jsのインストール

OSによってまちまちだと思うので、以降はNode.jsはすでに入っている前提で進めます。
入っていなければ公式からインストールします。

パスが通ってなければ通しておきます。

自分がインストールしたバージョンは以下です。

$ node -v
v6.9.4
MongoDBのインストール

同じく、以降はMongoDBはすでに入っている前提で進めます。
入っていなければ公式からインストールします。

インストールフォルダ(自分の環境だとC:/MongoDB/Server/3.4/bin)にパスを通しておきます。

インストールしたバージョンは以下です。

$ mongod --version
db version v3.4.4
git version: 888390515874a9debd1b6c5d36559ca86b44babd
OpenSSL version: OpenSSL 1.0.1u-fips  22 Sep 2016
allocator: tcmalloc
modules: none
build environment:
    distmod: 2008plus-ssl
    distarch: x86_64
    target_arch: x86_64

データベースは任意のディレクトリに保存出来るので、bin直下にmongodb.configを作成して、データベースの保存場所を指定します。

mongodb.config

dbpath=C:/MongoDB/data

上記を指定してmongodを起動します。

$ mongod --config "C:/MongoDB/Server/3.4/bin/mongodb.config"

これでデータベースが起動しました。

次回からは実際にMEANスタックを利用した開発に入っていきます。