Help us understand the problem. What is going on with this article?

メジャーなNode用Webアプリケーションフレームワークを比較する

More than 3 years have passed since last update.

Node界では個性的なWebアプリケーションフレームワークが乱立しており、RubyにおけるRailsのようなデファクトは存在していません。

フレームワーク選びの指針になるように、特徴や基本機能、人気度などを比較してみました。

今回比較対象とするフレームワークは以下の通りです。

Express

軽量なWebアプリケーションフレームワークです。MEANスタックの一角として扱われている通り、最も有名なNode用のフレームワークなのではないでしょうか。

Nodeをあまり隠さないシンプルな設計思想です。

Koa

Expressと同等のシンプルな設計のもと、ミドルウェアにES6流の非同期処理を追加したフレームワークです。

開発者はExpressと同じTJ Holowaychuk氏。

Hapi

Expressと同等のシンプルな設計のもと、コードを設定ファイルのように記述できるフレームワークです。

Sails

RailsライクでフルスタックなWebアプリケーションフレームワークです。

Express/Koa/Hapiは、MVCやヘルパーは後付けで拡張していく思想ですが、こちらはSails単体で全部載せです。

Meteor

SPA向けの、フルスタックなWebアプリケーションフレームワークです。

WebSocketでクライアントとサーバを常時接続にしつつ、リアクティブアプリケーションを最小限のコードで構築できます。

なお、今回比較に使用した各フレームワークのバージョンは以下の通りです。

  • Express : 4.13.1
  • Koa : 1.0.0
  • Hapi : 10.4.1
  • Sails : 0.11.2
  • Meteor : 1.2.0.2

開発者/利用者コミュニティ

Meteor Express Sails Koa Hapi
Github Star 28,905 21,022 12,163 7,309 4,853
StackOverflow 質問 15,886 17,933 3,568 228 71
Qiita 記事 80 201 63 20 5
npm 先月ダウンロード - 3,212,317 52,290 44,641 114,472
npm 依存パッケージ - 6,682 36 244 224

※ 2015/10/13調べ
※ Meteorはnpmによるインストールができないため、関連情報なし。

Expressが人気かと思いきや、MeteorがGithub Startではトップです。

とはいえExpressは依存パッケージやダウンロード数では他と比べて桁外れであり、存在感は半端ないですね。また、日本語情報(Qiita等)が多い点でも有利です。

Webサーバ生成・起動

フレームワークを使わずにWebサーバ(HTTPサーバ)を生成・起動するのは、以下のように書きます。

server.js
var http = require('http');
var server = http.createServer();

server.listen(3000);

Webアプリケーションを構築する場合はまずここから始まりますので、各フレームワークを使った場合にどのようになるのかを比べてみます。

Express

app.js
var express = require('express');
var app = express();

var server = app.listen(3000);

Expressのサーバ起動処理は、素のNodeと同じように読めるように作られてますね。

Koa

app.js
var koa = require('koa');
var app = koa();

var server = app.listen(3000);

KoaはExpressと全く一緒。

Hapi

index.js
var Hapi = require('hapi');

var server = new Hapi.Server();
server.connection({port: 3000});

server.start(function () {});

Express/Koaとだいたい一緒です。

Koaは元々Expressと同じ開発者によって設計されてますし、Hapiも後述するSailsもExpress上に実装されているそうです。

Sails

app.js
var sails = require('sails');
var rc = require('rc');

sails.lift(rc('sails'));

Sailsの場合、サーバ生成処理はフレームワークに隠蔽されて見えません。

ユーザが見えるコードの中で、強いて言えば上記のコードがサーバ起動ですかね。素のNodeとは全然違います。

sails.lift()というネーミングセンスは、キラキラネームっぽくもカッコいいと思います。

ポート番号指定は、アプリ内のconfigファイルで行うスタイルです。

Meteor

MeteorもSailsの場合と同様、サーバ生成処理は見えません。
ポート番号を変えて起動したい場合は、起動コマンドで下記のように引数として渡します。

$ meteor --port 13000

ルーティング+hello world

Express

get()post()にパスとコールバックを指定すれば、簡単なルーティングができます。

app.js
var express = require('express');
var app = express();

app.get('/', function(req, res) {
  res.send('Hello world');
});

Koa

やはりExpressと似てます。

app.js
var koa = require('koa');
var route = require('koa-route');
var app = koa();

app.use(route.get('/', function *() {
  this.body = 'hello world';
}));

違いで目立つのは、非同期処理を簡単に扱えるようにするgeneratorを返すために、function *()となっているところですね。

Hapi

server.route()の部分がルーティングですが、HTTPメソッド・パス・コールバックが一塊になっており、見た目が洗練されてます。

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

server.route({
  method: 'GET',
  path: '/',
  handler: function (request, reply) {
    reply('hello world');
  }
});

Sails

SailsはURLとコントローラのファイル名で勝手にルーティングしてくれます。

例えば以下のコマンドでhomeコントローラとindexアクションを生成すると、

$ sails generate controller home index

URLの/home/indexが、api/controllers/HomeController.jsindex()関数に、自動的にルーティングされます。

この当たりの手触り感はRailsっぽいですね。

index()に以下の処理を追加すればhello worldになります。

api/controllers/HomeController.js
module.exports = {
  index: function (req, res) {
    return res.send('hello world');
  }
};

Meteor

Meteor謹製のBlazeというViewライブラリを使用し、リアクティブプログラミングでUIを構築していくのがMeteor流です。

hello worldのような単純な例では、まず以下のようにhtml記述します。
ここではmessageに、jsからの設定値を反映させるようにしています。

sample.html
<head>
  <title>met</title>
</head>

<body>
  {{> hello}}
</body>

<template name="hello">
  <p>{{message}}</p>
</template>

次にjs側で、message()関数を定義してhtml側に反映させられるようにします。

sample.js
Template.hello.helpers({
  message: function () {
    // セッション情報の'message'内容を返す
    return Session.get('message');
  }
});

// 'message'の初期値をhello worldに設定
Session.setDefault('message', 'hello world');

たったこれだけでmessageデータとView側のmessageとの間でバインディングが実現でき、messageの値を変更すれば自動的にView側にも反映されます。(しかも画面全体の再レンダリングは発生しないという)

+α

さらに、各フレームワークの特徴的な機能を比べてみます。

Express

Expressは機能をmiddlewareとして構築していくのが特徴です。

自分で作りこむWebアプリも他ベンダーのツールも、Expressからは同じようにmiddlewareとして見えることになります。

以下はhello worldのコードですが、

app.js
var express = require('express');
var app = express();

app.get('/', function(req, res) {
  res.send('Hello world');
});

ルーティングを別ファイルにしてmiddlewareとして指定するには、以下のようにします。

こちらはルーティングファイルの利用側。

app.js
// indexのルーティングを取得
var routes = require('./routes/index');

// middlewareとして登録
app.use('/', routes);

こちらはルーティングファイル。routerにルーティングをmiddlewareとして登録後、利用側にみせるためにexports()しています。

routes/index.js
var express = require('express');
var router = express.Router();

// ルートフォルダへのGETリクエストへのルーティング
router.get('/', function(req, res, next) {
  res.send('hello world');
});

module.exports = router;

Koa

KoaはES6のgeneratorを使うことで、非同期処理を同期処理っぽく書けるようになっています。

home()/にルーティングされている関数ですが、中では非同期で動作するhelloAsync()を呼び出しています。

controllers/messages.js
// '/'で呼ばれるhome()関数
module.exports.home = function *home() {
  // 非同期処理を呼び出し
  this.body = yield helloAsync('hello world 1');

  // 非同期処理呼び出し後の処理
  console.log('hello world 2');
};

// 非同期処理。3秒後に受け取った文字列をダンプする
function helloAsync(hello) {
  return function (callback) {
    setTimeout(function () {
      console.log(hello)

      callback(null, 'hello world');
    }, 3000);
  };
};

非同期処理のhelloAsync()は受け取った'hello world 1'を3秒後にダンプするので、素朴に処理されると以下のようダンプ結果になりそうですが、

hello world 2
hello world 1

yield呼びのおかげで

hello world 1
hello world 2

と出力されます。(コードの見た目通りの順に処理されます)

DBとの接続やら何やらでコールバッグ地獄になりがちなWebアプリも、これならエレガントに書けますよね。

Hapi

Hapiはルーティングをロジック風ではなく設定ファイルっぽく書けてうれしいという話でしたが、例えばValidationも似たように書けます。

以下はnameというパラメータに3~10文字の文字数制限をつけているコードです。

index.js
var Hapi = require('hapi');
var Joi = require('joi');

var server = new Hapi.Server();

server.route({
  // ルーティング
  method: 'GET',
  path:'/hello/{name}',
  handler: function (request, reply) {
    reply('hello ' + request.params.name + '!');
  },
  // Validation
  config: {
    validate: {
      params: {
        name: Joi.string().min(3).max(10)
      }
    }
  }
});

JOIというValidation用のモジュールが秀逸で、見た目がかなり直感的になります。

JOIはExpress等、他のフレームワーク用のモジュールもあるようですが、やはり見た目のフィット感はHapiで使うのが一番な感じです。

Sails

hello worldの例ではControllerのみ生成していましたが、以下のようにすれば同時にModelも生成されます。

$ sails generate api user

これ一発で最小限なCRUDなAPIが完成。

アプリケーションを起動すると、もうモデルへの操作ができるようになってます。

curlで試験してみると、一通りのことができるようになっています。1行もコード書いてないのにスゴイ。

一覧
# 一覧表示するが、何もない
$ curl localhost:1337/user
[]
追加
# 1件追加した
$ curl localhost:1337/user/create

# 一覧表示すると、1件増えている
$ curl localhost:1337/user
[
  {
    "createdAt": "2015-10-12T14:26:43.886Z",
    "updatedAt": "2015-10-12T14:26:43.886Z",
    "id": 1
  }
]
表示
# IDを指定してアイテム表示
$ curl localhost:1337/user/1
{
  "createdAt": "2015-10-12T14:26:43.886Z",
  "updatedAt": "2015-10-12T14:26:43.886Z",
  "id": 1
}
更新
$ curl localhost:1337/user/update/1
{
  "createdAt": "2015-10-12T14:26:43.886Z",
  "updatedAt": "2015-10-12T14:28:40.072Z", # <- 日付更新
  "id": 1
}
削除
# IDを指定してアイテム削除
$ curl localhost:1337/user/destroy/1

# 消えた
$ curl localhost:1337/user
[]

Meteor

Meteorの場合、クライアントとサーバのコードの境界があいまいで、1つのjsファイルに両側のコードを書けちゃいます。

ここでは、helloという名前のボタンを押すと、サーバ側で定義したtry()という名前の関数をコールしています。

app.js
// クライアント側コード
if (Meteor.isClient) {
  Template.hello.events({
    // helloボタンクリックイベント時に、サーバ側のtryという名前の関数をコール
    'click button': function () {
      Meteor.call('try', 'world', function(error, result) {
        alert(result);
      });
    }
  });
}

// サーバ側コード
if (Meteor.isServer) {
  // 起動時初期化
  Meteor.startup(function () {
    Meteor.methods({
      // クライアント側から呼ばれる関数
      try: function(name) {
        return "Hello, " + name;
      }
    });
  });
}

気が付くとサーバ側とクライアント側で同じような処理を書いていて、しかもそれぞれ必要になっていた、ということはままあります。

Meteorの場合はこの当たりをすっきり書けそうです。

メリット & デメリット

かなり独断入りですが。

Express

メリット
  • 歴史が長く、実績豊富
  • 情報量が多い
  • ミドルウェアを他のフレームワークに流用しやすい(Koaには定型的な修正でもっていけるし、Sailsではそのまま使えるらしい)
デメリット
  • if (err)パターンは、わかりやすいが若干古い感じ(→改良版がKoa)
  • 大規模になると見通しが悪くなる(→改良版がHapi)

Koa

メリット
  • 先進的なミドルウェア構造
  • Expressとの相似性
デメリット
  • 日本語の情報量が少ない

Hapi

メリット
  • 見やすいコード
  • 成果物を流用しやすい
デメリット
  • 日本語の情報量が少ない

Sails

メリット
  • Rails流のMVC
  • 多機能
  • コーディング量が少なくて済む
デメリット
  • (Rails未経験者には特に)学習コストが高い

Meteor

メリット
  • SPA・リアクティブプログラミングを強力にサポート
  • 多機能
  • コーディング量が少なくて済む
デメリット
  • ベンダーロック感あり(View層にはMeteor謹製のBlazeの代わりにReact等も使えるらしいが、それはそれでイバラな予感がしなくもない)

じゃあどれを使えばいいの?

結局は「用途による」いう当り前な結論になりそうですが。。

汎用的で情報の多いExpress、美しさで攻めるならKoaかHapi。
フルスタックが好みの場合、SPAを作るならMeteor、そうでなければSailsっていう感じでしょうか。

Googleトレンド

思いついたので、Googleトレンドの結果を見てみます。

trand.png

うーむ、Express圧倒的ですね。これだとExpress一択なんじゃ、とか思ってしまいますが…

ググられる理由は色々だと思うので、参考情報程度に。

参考

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした