「AWS無料相談会」をオンラインで開催中

Node + ExpressでAPI開発をする時の開発環境

Node.js + ExpressでAPI開発をする時の環境について書きます。

(なぜNodeを選定したかについてはお客様の要望もありきのお話だったので、ここでは話題としません)。

概要

使用している技術は以下となります。

  • Node
  • Express
  • Docker
  • Yarn
  • Babel
  • Flow
  • MySQL
  • Sequelize
  • テスト
    • Mocha
    • supertest
    • ESLint
  • Circle CI

以下、順にみてゆきます。

Docker / Yarn

開発環境はDockerでコンテナ化しています。デバッグやツールの導入など柔軟に行えるよう、ubuntu環境にNodeをいれています。加えて、MySQLとnginxのコンテナをリンクしている感じです。

よくあるNodeのプロジェクトっぽく、yarnコマンドで全般的にできるようにしていて、例えば、アプリケーションコンテナ起動は以下のようにしています。

/* package.json */
{
    "docker:dev": "docker-compose -f docker-compose.yml -f docker-compose.dev.yml up",
    "docker:prod": "docker-compose -f docker-compose.yml -f docker-compose.prod.yml up"  
}

普段JSを使わない人は、yarnをローカルにインストールする必要があるので、不評もありました汗。ここはいい感じにコンテナに閉じられればそのほうがいいかもしれません。

Babel / Flow

ビルドというビルドもしていませんが、コンパイラとしてBabelだけいれています。Nodeは最新のECMAScriptであっても一通り対応していますが、Flow等のツールや、ライブラリ間の依存をなくすためにいれておきました。

実際のコードは、async/awaitを使って以下のように書くのがいいかなと思っています。

import type { $Request, $Response } from 'express';
import {db} from '../models';
import {response} from '../services';

const getResource = async (req: $Request, res: $Response) => {
  try {
    if (response.handleParameterError(req, res)) {
      return;
    }

    const resource = await db.Resource.findById(req.params.id);

    if (!resource) {
      response.notFound(res, 'リソースが見つかりませんでした。');
      return;
    }

    response.ok(res, {
      id: resource.id,
      name: resource.name,
      status: resource.status,
    });
  } catch (e) {
    response.internalServerError(res, e);
  }
};

静的型付けはFlowを使って行っています。

ORM

ORMとしてSequelizeをいれています。

migrationやバリデーションなど、最低限のことは一通りできますが、よくないなあと思うこともあったので、それはまた別の機会で取り上げてみようかと思います...。(TypeORMWaterlineなどを使ってみても良いかも?)。

テスト

テスト方針としては、以下のような具合で、コントローラーのテストを、結合テスト的に、リクエストとそのレスポンスをテストしています。
その他、重要なロジックのはいった部分では、別途関数のテストも行っています。

import request from 'supertest';
import assert from 'assert';
import {app} from '../app';

describe('/resources/:id', () => {
  beforeEach(async () => {
    await cleanDatabase();
    await mock.resources.single(); // DBにモックを生成
  });
  it('Resource詳細情報が取得できる', async () => {
    const req = request(app).get('/resources/1');
    await req
      .expect(200)
      .then((res) => {
        assert.equal(res.body.id, 1);
        assert.equal(res.body.name, 'NAME_0');
        assert.equal(res.body.status, 'valid');
      });
  });
});

また、eslintを使ってリントを行っており、型チェックも合わせて、Circle CIで全テストを実行しています。

入れておくと良さそうなライブラリ

Nodeの哲学として「一つのことをうまくやるのが良い」といわれますが、良くも悪くも、Expressはそれをよく表しています。

ひとつのライブラリは一つのことだけを行い、何かを実現したい時は別のモジュールを組み合わせる、ということで、いくつか最初から入れておくといいライブラリをピックアップしてみました。

  • body-parser - JSONなどのrequest bodyをいい感じに変換
  • express-validator - validatorをベースとした、パラメーターのバリデーション
  • morgan - HTTP request logger
  • bluebird - Promiseラッパー
  • lodash - オブジェクトや配列操作
  • moment - 時間操作
  • dotenv - 環境変数

以上、簡単にですが、NodeでAPI開発をする時の環境を書いてみました。参考になれば幸いです。