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サーバ)を生成・起動するのは、以下のように書きます。
var http = require('http');
var server = http.createServer();
server.listen(3000);
Webアプリケーションを構築する場合はまずここから始まりますので、各フレームワークを使った場合にどのようになるのかを比べてみます。
Express
var express = require('express');
var app = express();
var server = app.listen(3000);
Expressのサーバ起動処理は、素のNodeと同じように読めるように作られてますね。
Koa
var koa = require('koa');
var app = koa();
var server = app.listen(3000);
KoaはExpressと全く一緒。
Hapi
var Hapi = require('hapi');
var server = new Hapi.Server();
server.connection({port: 3000});
server.start(function () {});
Express/Koaとだいたい一緒です。
Koaは元々Expressと同じ開発者によって設計されてますし、Hapiも後述するSailsもExpress上に実装されているそうです。
Sails
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()
にパスとコールバックを指定すれば、簡単なルーティングができます。
var express = require('express');
var app = express();
app.get('/', function(req, res) {
res.send('Hello world');
});
Koa
やはりExpressと似てます。
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メソッド・パス・コールバックが一塊になっており、見た目が洗練されてます。
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.js
のindex()
関数に、自動的にルーティングされます。
この当たりの手触り感はRailsっぽいですね。
index()
に以下の処理を追加すればhello worldになります。
module.exports = {
index: function (req, res) {
return res.send('hello world');
}
};
Meteor
Meteor謹製のBlazeというViewライブラリを使用し、リアクティブプログラミングでUIを構築していくのがMeteor流です。
hello worldのような単純な例では、まず以下のようにhtml記述します。
ここではmessage
に、jsからの設定値を反映させるようにしています。
<head>
<title>met</title>
</head>
<body>
{{> hello}}
</body>
<template name="hello">
<p>{{message}}</p>
</template>
次にjs側で、message()
関数を定義してhtml側に反映させられるようにします。
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のコードですが、
var express = require('express');
var app = express();
app.get('/', function(req, res) {
res.send('Hello world');
});
ルーティングを別ファイルにしてmiddlewareとして指定するには、以下のようにします。
こちらはルーティングファイルの利用側。
// indexのルーティングを取得
var routes = require('./routes/index');
// middlewareとして登録
app.use('/', routes);
こちらはルーティングファイル。router
にルーティングをmiddlewareとして登録後、利用側にみせるためにexports()しています。
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()
を呼び出しています。
// '/'で呼ばれる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文字の文字数制限をつけているコードです。
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()
という名前の関数をコールしています。
// クライアント側コード
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トレンドの結果を見てみます。
うーむ、Express圧倒的ですね。これだとExpress一択なんじゃ、とか思ってしまいますが…
ググられる理由は色々だと思うので、参考情報程度に。