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
