Hapi.js ひとめぐり

Hapi.jsで簡単なAPIサーバーを構築するための基礎

基本

% npm i -S hapi
const Hapi = require('hapi');  
const server = new Hapi.Server();

server.connection({  
  host: 'localhost',
  port: 9000
});

server.route({  
  method: 'GET',
  path: '/',
  handler: (req, reply) => {
    reply('hello hapi');
  }
});

server.start(() => {  
  console.log(`Started at: ${server.info.uri}`);
});

基本的なサーバーはこのようなコードになります。

プラグイン

Hapiはプラグインで拡張していくタイプのフレームワークのようです。試しにgoodというロギングプラグインを試してみます。

% npm i -S good good-console
const Hapi = require('hapi');  
const server = new Hapi.Server();

server.connection({  
  host: 'localhost',
  port: 9000
});

let goodOptions = {  
  reporters: {
    console: [{
      module: 'good-console',
      args: [{ log: '*', response: '*' }]
    },
    'stdout']
  }
};

server.register({  
  register: require('good'),
  options: goodOptions
}, err => {

  server.route({
    method: 'GET',
    path: '/',
    handler: (req, reply) => {
      server.log('error', 'error!');
      server.log('info', 'replying');
      reply('hello hapi');
    }
  });

  server.start(() => {
    console.log(`Started at: ${server.info.uri}`);
  });
});

Hapiサーバーインスタンスのregister()を使ってプラグインを登録します。複数のプラグインを使う場合にはregisterに配列で渡してあげます。

server.register([  
  {
    register: require('good'),
    options: goodOptions
  }, {
    register: require('other-plugin'),
    options: { ...options }
  }
], err => {

その他、利用できるプラグインは公式にまとまっています。

Routing

上記の例のようにserver.route()に[HTTPメソッド、パス、ハンドラー]を定義したオブジェクト、またはその配列を渡すことでルーティングを定義できます。

server.route({  
  method: 'GET',
  path: '/',
  handler: (req, reply) => {
    reply('hello hapi');
  }
});

URLにパラメーターを付けたい場合は以下のような記述になります。

path: '/users/{userId}' // => /users/1234などにマッチ  
path: '/users/{userId?}' // => /users, /users/1234などにマッチ(userIdはオプションになる)  
path: '/users/{userId}.html' // => /users/1234.htmlなどにマッチ(1234は可変だが、.htmlは必須)  
path: '/{any*}' // => /users/1234、/hoge/fooなど、全てのURLにマッチ  

Reply, Request

replyを使ってレスポンスを返します。replyに返すデータの型に応じてレスポンスの形式を判断してくれます。

reply('hello world');     //=> content-type:text/html  
reply({ hello: 'hapi' }); // => content-type:application/json  

Promiseも

reply(Promise.resolve('hello hapi'));  

エラーオブジェクトを返したい場合にはboomというプラグインを利用することもできます。

% npm i -S boom
const Boom = require('boom');  
...

reply(Boom.notFound());  

Responseの操作

replyの返り値を使って、headercookieの操作ができます。

reply('hello world')  
    .code(418)                        // status code
    .type('text/plain')               // content type
    .header('x-powered-by', 'hapi!')  // header
    .state('some', 'data');           // cooki

Requestのライフサイクルイベント

Hapiではサーバに送られたリクエストは、その段階に応じて予め定義されているイベントを発火してくれます。

  1. onRequest ... Routerに処理される前の、リクエストを受け取った直後
  2. onPreAuth ... Router後、認証前
  3. onPostAuth ... 認証後
  4. onPreHandler ... ハンドラに処理される前
  5. onPostHandler ... ハンドラに処理された後
  6. onPreResponse ... レスポンスを返す直前

以下のように、各イベントのハンドラを定義します。

server.ext('onRequest', (request, reply) => {  
  // do something with request
  reply.continue();
});

reply.continue()を呼ばないと、そのイベントで止まってしまいレスポンスを返すまでに至らないことに注意します

その他

  • テンプレートエンジンを使ったビューを返したい: vision
  • ファイルを返したい場合にはinert
  • バリデートにはjoi