Node.jsのモジュールシステム
Node.jsはCommonJS(注1)をベースとしたモジュールの仕組みを採用しており、require()で他のモジュールをロードできるようになっています。この仕組みのおかげでアプリケーションを複数のファイルに分割して管理でき、全体の見通しをよくすることができます。このrequire()の仕組みはブラウザではサポートされていません。
ブラウザ以外でのJavaScriptを取り巻く仕様を決めるためのプロジェクト。モジュール仕様はその一部。Node.jsのモジュールシステムはCommonJSの一部であるモジュール仕様をベースとして作られた。
Node.jsとブラウザの両方に対応したライブラリを作る
Node.jsとブラウザのどちらにも対応したライブラリを作る時は、Node.jsではrequire()
で、ブラウザでは<script>タグで読み込めるよう、[リスト1]のように記述しておきます。ブラウザでは関数の内側にないthis
はグローバルオブジェクトのwindow
を指すため、this
のプロパティに代入すると他のスクリプトから参照できるようになります。
class MyLibrary # … if module?.exports # Node.jsの場合 module.exports = MyLibrary else # ブラウザの場合 @MyLibrary = MyLibrary
また、RequireJSで使われているAMDというモジュール定義方式に対応するには[リスト2]のように記述します。
class MyLibrary # … if define? and define.amd # AMDの場合 define -> MyLibrary # 使い方: require ['mylib'], (MyLibrary) -> else if module?.exports # Node.jsの場合 module.exports = MyLibrary # 使い方: MyLibrary = require 'mylib' else # ブラウザの場合 @MyLibrary = MyLibrary # 使い方: <script src="mylib.js"></script>
exportsとmodule.exportsの違い
他のモジュールから利用できるようにプロパティや関数などをエクスポートするにはexports
というオブジェクトに追加すればよいですが、代わりにmodule.exports
に追加してエクスポートすることもできます。一体このexports
とmodule.exports
は何が違うのでしょうか?
exports
とmodule.exports
は、それ自身に代入が可能かという点が異なります。exports
オブジェクトに追加したプロパティは正しくエクスポートされますが、exports
自体を上書きしてしまうと何もエクスポートされません。
exports = MyClass # 何もエクスポートされない
一方、module.exports
に値を代入すると問題なくエクスポートされ、代入した値がrequire()
の戻り値となります。エクスポートする関数をまとめて指定したい場合や、require()
の戻り値としてオブジェクト以外の値を返したい場合などにmodule.exports
に代入する方法を使います。もっとも、exports
の代わりにmodule.exports
を常に使っても問題ありません。
# ここはrequire()された時点で実行される console.log "モジュールをロード中" # require()で返されるオブジェクト module.exports = setup: -> console.log "setup" myfunc: -> console.log "myfunc"
console.log "step 1" mymod = require './mymod' console.log "step 2" mymod.myfunc()
step 1 モジュールをロード中 step 2 myfunc
一方、exports
のプロパティとして代入する場合にはmodule.exports
と記述する必要はありません。リスト4はmodule.exports
を使わずにリスト7のように記述できます。
# ここはrequire()された時に実行される console.log "モジュールをロード中" # エクスポートする関数をexportsに追加していく exports.setup = -> console.log "setup" exports.myfunc = -> console.log "myfunc"
URLからコンテンツを取得する
Node.jsの標準APIを使ってURLからコンテンツを取得するにはリスト8のように少し面倒なコードを書く必要があります。
http = require 'http' http.get host: 'ja.wikipedia.org' path: '/wiki/%E6%9D%B1%E4%BA%AC' , (res) -> if res.statusCode is 200 body = '' res.setEncoding 'utf8' res.on 'data', (chunk) -> body += chunk res.on 'end', -> console.log body else console.log "error: #{res.statusCode}"
Requestというモジュール(Apacheライセンス2.0)を使うと、より短いコードでコンテンツを取得できます。Requestをインストールするには、プロジェクトのディレクトリで次のコマンドを実行します。
$ npm install request
Requestを使うと、リスト8と同様の単純なGETリクエストをリスト9のように記述することができます。
request = require 'request' request url: 'http://ja.wikipedia.org/wiki/%E6%9D%B1%E4%BA%AC' , (err, response, body) -> throw err if err # 接続エラーなどが発生した場合 if response.statusCode is 200 # ステータスコードが「OK」の場合 console.log body else console.log "response error: #{response.statusCode}"
request()
の第1引数でURLなどのパラメータを指定します。ここでqs
にオブジェクトを指定すると、URLの?以降のクエリストリングをオブジェクトで指定することができます。
url: 'http://localhost/path' qs: abc: 123 def: 456
これはhttp://localhost/path?abc=123&def=456へのリクエストとなります。
また、POSTリクエストの場合はリスト10のようにrequest.post()
メソッドを使用します。form
パラメータをオブジェクトで指定すると、HTMLでフォームを送信した場合のようにname1=value1&name2=value2
の形式でPOSTデータが送信されます。リクエストのContent-Typeは自動的にapplication/x-www-form-urlencoded; charset=utf-8
となります。
request.post url: 'http://localhost/post' form: abc: 123 def: 456 , (err, response, body) ->
このリクエストで送信されるPOSTデータは以下のようになります。
abc=123&def=456
また、jsonパラメータをオブジェクトで指定した場合、JSON文字列に変換されてPOSTデータが送信されます。リクエストのContent-Typeは自動的にapplication/json
となります。
request.post url: 'http://localhost/post' json: abc: 123 def: 456 , (err, response, body) ->
このリクエストで送信されるPOSTデータは以下のようになります。
{"abc":123,"def":456}
その他、任意のPOSTデータを送信するにはbody
パラメータで文字列を指定します。
request.post url: 'http://localhost/post' body: 'something' , (err, response, body) ->
バイナリデータなどを送信する場合にはBufferオブジェクトをbody
パラメータに指定することができます。
fs = require 'fs' # ファイルを読み込みBufferオブジェクトを取得する imageData = fs.readFileSync 'image.png' request.post url: 'http://localhost/post' body: imageData , (err, response, body) ->
バイナリデータの受け取り
レスポンスはデフォルトでUTF-8として解釈された文字列となりますが、Bufferオブジェクトで受け取りたい場合はencoding
パラメータにnullを指定します。
request url: 'http://localhost/image.png' encoding: null , (err, response, body) -> throw err if err if response.statusCode is 200 process.stdout.write body else console.log "response error: #{response.statusCode}"
タイムアウト
リクエストにタイムアウトを設定するにはtimeout
をミリ秒で指定します。タイムアウトが発生した場合、コールバックのerr
引数に値が入ります。
request url: 'http://localhost/' timeout: 30000 # 30秒でタイムアウト , (err, response, body) -> throw err if err