基礎から学ぶNode.js

第3回Express.jsを使ったWebアプリケーションを構築

はじめに

前回までは、Node.jsについての基礎的な説明からnpmによるパッケージ管理まで、紹介してきました。今回から、いよいよNode.jsを使ったWebアプリケーションを実装していきます。前々回に単純なHTTPサーバの実装についてコードとともに紹介しました。あれをみるとわかるとおり、nodeが提供するHTTPサーバの機能はプロトコルに沿ったローレベルなものになっています。例えば、リクエストが来たら、そのURLパスに従った処理を実行するために、リクエストURIの解析から実装していかなくてはなりません。そういったことが簡単に定義して処理を書けるWebアプリケーションフレームワークがnodeにも存在します。本連載では、その代表格のExpressを使って実装してみたいと思います。

Expressのインストール

Express(Express.jsと表記されることが多い)は、非常に手軽にnodeを使ったWebアプリケーションを作成できます。まずインストールからです。npmを使ってこちらも簡単に行えます。生成ツールとしてその名の通りのコマンドexpressも提供するのでグローバルオプション付きで行ったほうが良いでしょう。

npm install -g express

expressはそれ自体が有用なライブラリを組み合わせて構成されています。HTMLやCSSのテンプレートエンジンは、選択することができます。Expressのバージョンは、執筆時点で最新の安定版である2.5.9を使用します。

ExpressでWebアプリの雛形を生成

HTMLテンプレートエンジンはJade、EJSやHaml.jsなどを、CSSテンプレートエンジンはStylusなどを選べます。今回は、HTMLのテンプレートエンジンにJSPやPHPと同じように使えるEJS(Embedded JavaScript)を、CSSのテンプレートエンジンはとくに今回は使わないので指定しません。

express -t ejs firstapp

とコマンドラインから実行するとカレントディレクトリにfirstappというディレクトリが作成されその中に一通りのファイルが生成されます。

このままだと依存するモジュールファイルがないので、

cd firstapp
npm install -d

として利用するモジュールをインストールします。

この時点で、

node app.js

とすることで、ポート3000番でnodeによるWebサーバが起動します。

ブラウザから「http://localhost:3000/」にアクセスすると、Welcome to Express のページが表示されることが確認できます。

では、app.jsの中身をみていきましょう。

/**
 * Module dependencies.
 */

var express = require('express')
  , routes = require('./routes');

var app = module.exports = express.createServer();

// Configuration

app.configure(function(){
  app.set('views', __dirname + '/views');
  app.set('view engine', 'ejs');
  app.use(express.bodyParser());
  app.use(express.methodOverride());
  app.use(app.router);
  app.use(express.static(__dirname + '/public'));
});

app.configure('development', function(){
  app.use(express.errorHandler({ dumpExceptions: true, showStack: true }));
});

app.configure('production', function(){
  app.use(express.errorHandler());
});

// Routes
app.get('/', routes.index);

app.listen(3000, function(){
  console.log("Express server listening on port %d in %s mode", app.address().port, app.settings.env);
});

先頭のModule dependencies項では、第1回で説明したとおり、モジュールのロードになります。expressモジュールをまずロードし、次にroutes/index.jsをロードします。拡張子.js自体が省略可能で、さらにindexも省略されています。これは紛らわしいので、ファイル名を「routes/main.js」にリネームして、requireの引数も⁠./routes/main⁠と変更します。

続く処理は、expressのサーバ生成メソッドでappという変数にWebサーバの機能を参照するインスタンスをセットしています。

さらにConfigurationの項で、app.configureメソッドのコールバックで呼ばれる無名関数の中で、基本的な設定が行われています。この時点で確認しておきたいのは、viewsとしてテンプレートエンジンのファイルが置かれるディレクトリが、app.jsのある場所をカレントディレクトリとしてその中のviewsであること、静的ファイルが置かれるのは、publicであることです。画像ファイルやクライアント側のJavaScriptファイルといったものは、このpublicに置きます。そうすると、nodeのHTTPサーバからpublicをドキュメントルートとして参照できます。

その次のconfigureメソッドでは開発環境(development)とリリース環境(production)それぞれに限定した設定を定義します。初期定義では例外発生時のスタックトレースを開発環境でのみ出力するようにしています。諸々のビューキャッシュ機構などはproduction時のみ有効になります。これは、開発時はnodeの再起動無しにviews/内のテンプレートを編集してもそのまま反映される、ということを意味します。

Routesの項では、appのHTTPメソッド名と同様のメソッドget, post, put, delメソッド(DELETEに相当)を使ってそれぞれのHTTPメソッドと第1引数のリクエストパスに対して、第2引数の関数を呼び出すように紐付けします。

app.get('/', routes.index)は、先ほどリネームしたroutes/main.jsのexports.indexとして定義した関数を、⁠GET /」にバインドしています。

アプリケーションの実装

本連載では、最終的に簡単なミニブログを作ってみたいと思います。データベースの利用は、次回からとし、今回はソースにそのまま定義したデータを返し、登録処理で受け取った情報をコンソールに出力してみます。

まずroutes/main.jsを次のように書き換えます。リクエスト「GET /」では、itemsの中身をEJSを使ってレンダ(表示)しています。

var items = [
    { "text": "1st Post." }
  , { "text": "2nd Post." }
];

exports.index = function(req, res){
  res.render('index', { title: 'Entry List', items: items })
};

具体的にはresを通して、レスポンスを処理します。renderメソッドの第1引数にはviews内のindex.ejsを利用するように指定しています(拡張子.ejsは省略⁠⁠。さらに第2引数に渡したJSONのKey-ValueがEJSファイル内でそのままキーがグローバル変数名となり、Valueが参照できます。

では、対象テンプレートのviews/index.ejs を書き換えます。

<h1><%= title %></h1>
<p><a href="/form">New Entry</a></p>
<ul>
<% items.forEach(function(item){ %>
  <li><%= item.text %></li>
<% }) %>
</ul>

「<%= title %>」は、title変数の中身をそのまま出力することを示します。さらに「<%~%>」を使って、そのままJavaScriptで処理を書くことができます。itemsの中身をforEachでループして、リストに展開しています。

登録フォームの実装

先のページ内で「New Entry」リンクで呼び出す「GET /form」で表示するフォームページを作成します。views/form.ejsを次の内容で作成します。

<form method="POST" action="/create">
 <fieldset>
  <p><label>text</label><input type="text" name="text"></p>
 </fieldset>
 <input type="submit" value="Post">
</form>

フォームページの表示処理と、⁠POST /create」で受け取った値をコンソールに書き出す処理を、routes/main.jsに追記します。

exports.form = function(req, res){
  res.render('form', { title: 'New Entry' })
};

exports.create = function(req, res){
  console.log(req.body.text);
  res.redirect('/');
};

リクエストの情報にはreqを通してアクセスします。req.bodyには、jsonやx-www-form-urlencodedタイプのデータをパースしたものが入っているため、パラメータtextは、req.body.textで参照できます。また、クエリは、⁠/path?text=example」がreq.query.textとして値「example」を参照できます。投稿内容を受け取った後は、インデックスページにリダイレクトしています。

以上、フォームページ表示とcreateリクエストのバインドをapp.jsに定義します。

app.get('/form', routes.form);
app.post('/create', routes.create);

ここまでできたら、nodeを起動して再びブラウザから確認してみましょう。

図1 インデックスページ
図1 インデックスページ

インデックスページのNew Entryリンクをクリックします。

図2 フォーム画面
図2 フォーム画面

フォーム画面が表示されます。textフィールドに入力した値は、Postするとコンソールに表示されます。画面はインデックスページに戻ります(本来はここで投稿した内容が反映されます⁠⁠。

最後に

今回は、Expressを使ったWebアプリケーションの作成について、セットアップから基本動作まで説明しました。次回は、データベースにMongoDBを使い、具体的なモデルの実装を進めていきます。

おすすめ記事

記事・ニュース一覧