鍋綿ブログ

C#・SharePoint・SharePoint Framework・Office365を中心に扱うブログです。

nodejs+express+typescriptでRESTfulなAPI構築を試してみた

Node.js入門者の私がサーバーサイドを構築してみました。
expressを使った構成ですが、
公式サンプルは生のjavascriptでした。
今回はtypescriptにしたかったので少々手間取りましたが
出来上がってしまえばシンプルでした。
Yeomanジェネレーターも作成しましたのでよろしければどうぞ。

 

 

環境構成

  • Node.js 8.11.3
  • TypeScript (tsc) 3.2.2
  • Visual Studio Code

出来上がったソースコード

GitHubにアップしました。

github.com

Yeomanジェネレーター

Yeomanとは

Yeomanは、コードの雛形を作成するツールです。
Node.jsでの開発は依存関係が多くなりがちで、
毎回npm install したり tsconfig.json や gulpfile.js を書いたりするのは大変ですので、
すぐに開発に入れるようにするには作成済の雛形を持ってくるほうが楽です。

と、いうわけで、今回作ったコードをYeomanの雛形 (ジェネレーター) として登録してみました。
今後のお仕事やお勉強が捗る・・と、いいな。

利用方法

まず最初にYeoman自体をインストールする必要があります。
インストールはコマンドプロンプトからnpmコマンドで行います。
(前述の環境が揃っている前提)

npm install yeoman -g

次にYeomanのジェネレーターをインストールします。

npm install generator-typed-express-restfulapi -g

ここまでで環境は揃いました。次に雛形を使ったコード生成を行います。
まず、コマンドプロンプトで任意のフォルダに移動します。

cd C:\work\YoGenerator\sampleApi

yo (雛形名) コマンドでコードを生成します。
コードはカレントディレクトリに生成されます。

yo typed-express-restfulapi

パッケージ名を聞かれますので任意の文字列を入力します。
何も入力せずEnterを押すと「restApi」というパッケージ名が採用されます。

f:id:micknabewata:20190105134122p:plain

Yeomanジェネレーターでコードを生成

ここまで出来たら、Visual Studio Codeでフォルダを開きましょう。
今開いているコマンドプロンプトでそのまま code . とすると楽です。
以降はVSCodeでの操作になります。

さて、今回作成したジェネレーターには
パッケージ自体 (node_modulesフォルダ) が含まれていません。
よってnpm installコマンドで持ってきます。

f:id:micknabewata:20190105134739p:plain

VSCodeのターミナルからnpm install

ここまでの操作で、画像のとおりnode_modulesフォルダが作成されるはずです。
npm startコマンドでローカルサーバーを起動します。

f:id:micknabewata:20190105135117p:plain

npm startコマンド

ブラウザでURLを叩いて結果を確かめます。

http://localhost:3000/api/example?param1=value1

f:id:micknabewata:20190105135241p:plain

ブラウザでURLを叩いて結果を確認

ユニットテストも通してみます。
VSCodeのターミナルで実行中のnpm startをCtrl + Cで強制終了し、
npm test を実行しましょう。 

f:id:micknabewata:20190105135741p:plain

npm test 実行結果

これで動作確認まで終わりです。
あとは自前の処理を追加していきます。

ソース解説

エンドポイントを追加するには

src / routes / api 配下にクラスを追加します。
複雑なものでなければApiBaseを継承し、
protectedなメソッドをオーバーライドすると楽です。

クラスを追加したらsrc / app.tsファイルを編集して
URLマッピングを追加します。

エンドポイント用のモデルクラスはsrc / models クラスに作りましょう。

API構築のコア部分 (express)

Node.jsでWebアプリケーションやWeb APIを実装するためのモジュールです。
設計の自由度が高く、既定では薄い実装となっており
ミドルウェアを追加して機能を追加していくといった使い方になります。

src / app.ts と src / www にexpressでのサーバー起動に関する処理を固めてあります。

セキュリティ確保のためのHTTPヘッダ付与 (helmet)

helmetというモジュールで一括追加してくれます。
利用方法は簡単で、npm install helmet でインストールして
src / app.ts にて import、からのuseするだけです。
以下、重要箇所のみ抜粋します。

import * as helmet from 'helmet';

export default class App {
    private expressApp = express();

    constructor() {this.expressApp.use(helmet());
        ~
    }
}

以下が参考になりました。

Node.jsのセキュリティ・チェックリスト | POSTD

コンソールへのログ出力 (morgan)

サーバー実行中にログを出力してくれるモジュールです。
利用方法はhelmetとほぼ同じです。

import * as logger from 'morgan';

export default class App {
    private expressApp = express();

    constructor() {this.expressApp.use(logger('dev'));
        ~
    }
}

これも同様。ミドルウェアの利用は簡単ですね。

import * as cookieParser from 'cookie-parser';

export default class App {
    private expressApp = express();

    constructor() {this.expressApp.use(cookieParser());
        ~
    }
}

Cookieの格納例は src / routes / api / example.ts にあります。

export default class Example extends ApiBase {
    private expressApp = express();

    protected getEvent(req : express.Request, res : express.Response, next : express.NextFunction)
    {
        // Cookie生成サンプル

        // 現在設定されているCookieから sample というキーの値を取得する
        // キーが無い場合、'set ok' という固定値を生成
        var sampleValue = req.cookies ? req.cookies.sample || 'set ok' : 'set ok';

        // Cookieにsampleというキーをセットする
        res.cookie(
            'sample',
            sampleValue, 
            {
                // 有効期限を15分間にする
                expires : new Date(Date.now() + 90000),

                // JavaScriptから取得不可能にする(Cookieを盗むJSへの対策)
                httpOnly : true
            });
    }
}

ポイントはCookie格納時のオプションでhttpOnly : trueとしているところです。
悪意のあるウェブサイトからCookie情報を抜き取られないように対策しています。

タスク自動化 (gulp / gulp-typescript / gulp-uglify)

gulpfile.jsを作成し、buildコマンドを定義しました。
これで、gulp build というコマンドで定義したアクションが実行される状態になりました。

gulpfile.js内では、src配下で開発したファイルをbinフォルダにまとめています。
.tsファイルはトランスパイルを行って.jsファイルにしています。
.jsファイルはuglifyで圧縮しました。

これをpackage.jsonに記述したprestartスクリプトで流しています。

テスト (mocha / chai / sinon)

テストコードはtestフォルダ内に作成しました。
テストランナーはmochaとし、chaiでアサートしています。

また、リクエストやレスポンスを生成するのが面倒なので
必要なメソッドだけをsinonでスタブ化しました。
その他プロパティ群はPartialで生成しています。