Hapi.jsでHappy Coding - Part3: プラグインをつくる


MongoDBへのCRUD操作とSwagger UIからAPIをテストするプロジェクトHapi.jsで作成しました。app.jsのRouteの処理が長くなったので、別ファイルに移動しようと思います。Hapi.jsではPluginsの仕組みで機能拡張ができます。Swaggerのインタフェースを提供するhapi-swaggerはnpmからパッケージをインストールしましたが、プラグインはアプリ内にも簡単に作ることができるのでRoute処理をプラグインにリファクタリングします。

公開されているプラグインはこちらのページに用途別にまとまっています。

プロジェクト

Routeのプラグインを作成したリポジトリはこちらです。ディレクトリは以下のようになっています。

$ cd ~/node_apps/docker-hapi
$ tree -L 2
.
├── Dockerfile
├── app.js
├── docker-compose.yml
├── models
│   └── job.js
├── mongo
├── node_modules -> /dist/node_modules
├── npm-debug.log
├── package.json
└── routes
    └── jobs.js

今までと同様にDocker Composeを使いMongoDBとlinkしています。

/node_apps/docker-hapi/docker-compose.yml
hapi:
  build: .
  ports:
    - 3000:3000
  volumes:
    - .:/app
  links:
    - mongo
mongo:
  image: mongo
  volumes:
    - ./mongo:/data/db

routes/jobs.js

routes/jobs.jsが作成したプラグインです。長くなるのでPOST以外は省略します。今までapp.jsに定義していたRouteの処理をexports.registerの関数内に移動しました。

~/node_apps/docker-hapi/routes/jobs.js
'use strict';
var Joi = require('joi');
var JobModel = require('../models/job');

exports.register = function(server, options, next) {

    server.route({
        method: 'POST',
        path: '/api/jobs',
        config: {
            tags: ['api'],
            description: 'Save job data',
            notes: 'Save job data',
            validate: {
                payload: {
                    name: Joi.string().required(),
                    query: Joi.string().required()
                }
            }
        },
        handler: function(request, reply) {
            var job = new JobModel(request.payload);
            job.save(function(error) {
                if(error) {
                    reply({
                        statusCode: 503,
                        message: error
                    });
                } else {
                    reply({
                        statusCode: 201,
                        message: 'Job Saved Successfully'
                    });
                }
            });
        }
    });
...
    // callback to complete
    next();
}

exports.register.attributes = {
    name: 'jobs-route'
}

重要なのは関数の最後にコールバックのnext()を実行してこのプラグインでの処理の終了を伝えることです。またexports.register.attributesにこのプラグインの名前を定義しておきます。

app.js

Routeの処理を別ファイルにプラグインとして移動したのでapp.jsの構成はすっきりと短くなりました。プラグインの登録はserver.registerを実行して行います。Swaggerのプラグインと加えて2つになったのでplugins配列に格納します。

~/node_apps/docker-hapi/routes/app.js
'use strict';

var Hapi = require('hapi'),
    server = new Hapi.Server(),
    mongoose = require('mongoose');

mongoose.connect('mongodb://'+process.env.MONGO_PORT_27017_TCP_ADDR+':'
              +process.env.MONGO_PORT_27017_TCP_PORT+'/jobdb');
server.connection({port: 3000});

var plugins = [
    { register: require('hapi-swagger'),
      options: {
          apiVersion: "0.0.1"
      }
    },
    { register: require('./routes/jobs')}
];

server.register(plugins, function(err){
    if (err) throw err;
    server.start(function (){
        console.log('Server running at:', server.info.uri);
    });
});

プログラムの実行

Docker Composeからhapiサービスを実行します。

$ docker-compose up hapi
Recreating dockerhapi_mongo_1...
Recreating dockerhapi_hapi_1...
Attaching to dockerhapi_hapi_1
hapi_1 |
hapi_1 | > [email protected] start /app
hapi_1 | > node app.js
hapi_1 |
hapi_1 | Server running at: http://803cef3ac3ca:3000

Routeをプラグイン化してもSwaggerUIからMongoDBへのCRUD処理はできるのでリファクタリングは成功です。