node.jsによるAPIサーバ構築
                     h_mori




13年4月18日木曜日
自己紹介
              • 森 英寿(プログラマ)
                 hidetoshi.mori
                 @h_mori

               SOICHA/TweetMe
               ATND暦/生存連絡



13年4月18日木曜日
背景

              • iOSアプリから利用するAPIサーバ構築
              • API認証基盤を作る
              • PushNotificationを発行


13年4月18日木曜日
node.jsを利用した経緯

              • MongoDBのRESTオプションによる構築
              • 後から認証基盤を追加実装
              • 後からPushNotificationを実装


13年4月18日木曜日
データモデル
              • DataList (トランザクション)
              • User (アカウント情報) ※追加
              • Device (端末情報) ※追加
              • Group (ユーザカテゴリ) ※追加

13年4月18日木曜日
サーバ構成

              • MongoDB
              • restify
              • Mongoose


13年4月18日木曜日
MongoDB?

         • NoSQL
         • ドキュメント指向DB(スキーマレス)
         • where, like検索, 集計関数も可能


13年4月18日木曜日
苦手なこと
         • トランザクション制御
          (アトミック性は保証)

         • リレーショナル、一意制約等


13年4月18日木曜日
DB構造

         • DBS > Collection > Object
              (Database)   (Scheme)   (Record)




13年4月18日木曜日
メリット

         •    起動オプションにREST I/Fがある

         •    JSONとの親和性が高い

         •    WebAPI化しやすい




13年4月18日木曜日
有用なケース

         •    トランザクション制御が不要

         •    大量書き込みが想定される

         •    データの一意制約が不要




13年4月18日木曜日
導入

         •    $ brew install mongodb

         •    $ port install mongodb

         •    mongodb.orgからDLして任意のフォルダに展開




13年4月18日木曜日
DBサーバ起動


         •    $ ./bin/mongod -dbpath=db/ --rest
               ※RESTオプション付き

               ※28017ポートでWebAPIが起動




13年4月18日木曜日
DBクライアント



         •    $ ./bin/mongo
               GUI clientも存在するがメリットは薄い




13年4月18日木曜日
URLアクセス


         •    curl 'http://localhost:28017/testdb/hoge'

         •    curl -d "{'user':'test'}" 'http://localhost:28017/testdb/hoge'




13年4月18日木曜日
restify ?

              • RESTに特化したnode.jsモジュール
              • Expressを軽量化したようなもの


13年4月18日木曜日
restifyの主な機能
              •   Routing

              •   Header Parser / Error handler

              •   JSONP/JSON support

              •   GZIP support

              •   URL-encoder

              •   multipart form



13年4月18日木曜日
restify導入


              • $ npm install restify
              • var restify = require('restify');


13年4月18日木曜日
Routing
              var restify = require('restify');
              var server = restify.createServer();
              server.use(restify.queryParser());
              function send(req, res, next) {
                 res.send('hello ' + req.params.name);
                 return next();
              }
              server.post('/hello', function create(req, res, next) {
                 res.send(201, Math.random().toString(36).substr(3, 8));
                 return next();
              });
              server.get('/hello/:name', send);
              server.head('/hello/:name', send);
              server.del('hello/:name', function rm(req, res, next) {
                res.send(204);
                return next();
              });



13年4月18日木曜日
Mongoose ?
              • MongoDB用ドライバ
              • Object Modeling Tool (O/Rマッパ)
              • DBコネクションのバッファリング
              • Validator、Defaults、Index等の定義可

13年4月18日木曜日
Mongooseの使用例
              var UserSchema = new mongoose.Schema({
                  email:{
                     type: String,
                     unique: true,
                     index: true
                  },
                  password:String,
                  created_at:{type: Date, default: Date.now},
                  updated_at:{type: Date, default: Date.now}
              });
              UserSchema.pre('save', function(next) {
                  this.updated_at = Date.now();
                  next();
              });
              var User = mongoose.model('user', UserSchema);




13年4月18日木曜日
Mongoose導入


              • $ npm install mongoose
              • var mongoose = require('mongoose');


13年4月18日木曜日
cloud9 IDE
              • ブラウザベースIDE
              • クラウド上にプライベート領域を作れる
               (github,bitbucket,heroku等と連携)

              • Node.js、PHP、Java、Ruby等をサポート

13年4月18日木曜日
cloud9 IDE 導入

              • ローカル環境にインストールも可能
              • $ git clone https://github.com/ajaxorg/
                cloud9.git cloud9
              • sudo ./cloud9/bin/cloud9.sh


13年4月18日木曜日
cloud9 IDEの起動


              • ./bin/cloud9.sh -w {workspace_dir} で起動
              • http://localhost:3131 にアクセス


13年4月18日木曜日
認証方式


              • SSL + Basic認証 + sha1(solt)



13年4月18日木曜日
restify.authorizationParser
              • server.use(restify.authorizationParser());
              • req.authorization オブジェクト
                {
                    scheme: <Basic|Signature|...>,
                    credentials: <Undecoded value of header>,
                    basic: {
                      username: $user
                      password: $password
                    }
                }




13年4月18日木曜日
認証基盤の実装
              server.use(function authenticate(req, res, next) {
                  if (!req.authorization.basic) {
                      res.header('WWW-Authenticate', 'Basic realm=""');
                      res.send(401);
                      return next(false);
                  }
                  User.findOne({email:req.username}, function (err, user) {
                      if (err) { return next(err); }
                      if (!user && user.password !== req.authorization.basic.password) {
                          return next(new restify.NotAuthorizedError());
                      }
                      return next();
                  });
              });




13年4月18日木曜日
実装上の注意
              • I/O系関数は処理順序を保証しない
               var user = User.findOne({email:req.params.email});
               user.password = req.params.password;
               user.save();
               res.send(user);




13年4月18日木曜日
実装上の注意
              • 必ずコールバックにて処理
               User.findOne({email:req.params.email}, function(err, user) {
                 user.password = req.params.password;
                 user.save(function(arr, data)) {
                      res.send(data);
                 });
               }




13年4月18日木曜日
実装上の注意
              • 複雑な処理の同期が必要な場合は外部
               モジュールを検討する
               ‣ async.js
               ‣ node-fibers
               ‣ flow-js
               ‣ step

13年4月18日木曜日
実装して思ったこと

              •   非同期処理が不要な場合node.jsは向かない 
                  (client, middlewareにロジックを置く場合は有)

              •   MongoDBと簡易JsonAPIは相性がいい

              •   node.jsと関連モジュールはまだまだ過渡期




13年4月18日木曜日

Nodejsによるapiサーバ構築事例

  • 1.
    node.jsによるAPIサーバ構築 h_mori 13年4月18日木曜日
  • 2.
    自己紹介 • 森 英寿(プログラマ) hidetoshi.mori @h_mori SOICHA/TweetMe ATND暦/生存連絡 13年4月18日木曜日
  • 3.
    背景 • iOSアプリから利用するAPIサーバ構築 • API認証基盤を作る • PushNotificationを発行 13年4月18日木曜日
  • 4.
    node.jsを利用した経緯 • MongoDBのRESTオプションによる構築 • 後から認証基盤を追加実装 • 後からPushNotificationを実装 13年4月18日木曜日
  • 5.
    データモデル • DataList (トランザクション) • User (アカウント情報) ※追加 • Device (端末情報) ※追加 • Group (ユーザカテゴリ) ※追加 13年4月18日木曜日
  • 6.
    サーバ構成 • MongoDB • restify • Mongoose 13年4月18日木曜日
  • 7.
    MongoDB? • NoSQL • ドキュメント指向DB(スキーマレス) • where, like検索, 集計関数も可能 13年4月18日木曜日
  • 8.
    苦手なこと • トランザクション制御  (アトミック性は保証) • リレーショナル、一意制約等 13年4月18日木曜日
  • 9.
    DB構造 • DBS > Collection > Object (Database) (Scheme) (Record) 13年4月18日木曜日
  • 10.
    メリット • 起動オプションにREST I/Fがある • JSONとの親和性が高い • WebAPI化しやすい 13年4月18日木曜日
  • 11.
    有用なケース • トランザクション制御が不要 • 大量書き込みが想定される • データの一意制約が不要 13年4月18日木曜日
  • 12.
    導入 • $ brew install mongodb • $ port install mongodb • mongodb.orgからDLして任意のフォルダに展開 13年4月18日木曜日
  • 13.
    DBサーバ起動 • $ ./bin/mongod -dbpath=db/ --rest  ※RESTオプション付き ※28017ポートでWebAPIが起動 13年4月18日木曜日
  • 14.
    DBクライアント • $ ./bin/mongo GUI clientも存在するがメリットは薄い 13年4月18日木曜日
  • 15.
    URLアクセス • curl 'http://localhost:28017/testdb/hoge' • curl -d "{'user':'test'}" 'http://localhost:28017/testdb/hoge' 13年4月18日木曜日
  • 16.
    restify ? • RESTに特化したnode.jsモジュール • Expressを軽量化したようなもの 13年4月18日木曜日
  • 17.
    restifyの主な機能 • Routing • Header Parser / Error handler • JSONP/JSON support • GZIP support • URL-encoder • multipart form 13年4月18日木曜日
  • 18.
    restify導入 • $ npm install restify • var restify = require('restify'); 13年4月18日木曜日
  • 19.
    Routing var restify = require('restify'); var server = restify.createServer(); server.use(restify.queryParser()); function send(req, res, next) { res.send('hello ' + req.params.name); return next(); } server.post('/hello', function create(req, res, next) { res.send(201, Math.random().toString(36).substr(3, 8)); return next(); }); server.get('/hello/:name', send); server.head('/hello/:name', send); server.del('hello/:name', function rm(req, res, next) { res.send(204); return next(); }); 13年4月18日木曜日
  • 20.
    Mongoose ? • MongoDB用ドライバ • Object Modeling Tool (O/Rマッパ) • DBコネクションのバッファリング • Validator、Defaults、Index等の定義可 13年4月18日木曜日
  • 21.
    Mongooseの使用例 var UserSchema = new mongoose.Schema({ email:{ type: String, unique: true, index: true }, password:String, created_at:{type: Date, default: Date.now}, updated_at:{type: Date, default: Date.now} }); UserSchema.pre('save', function(next) { this.updated_at = Date.now(); next(); }); var User = mongoose.model('user', UserSchema); 13年4月18日木曜日
  • 22.
    Mongoose導入 • $ npm install mongoose • var mongoose = require('mongoose'); 13年4月18日木曜日
  • 23.
    cloud9 IDE • ブラウザベースIDE • クラウド上にプライベート領域を作れる (github,bitbucket,heroku等と連携) • Node.js、PHP、Java、Ruby等をサポート 13年4月18日木曜日
  • 24.
    cloud9 IDE 導入 • ローカル環境にインストールも可能 • $ git clone https://github.com/ajaxorg/ cloud9.git cloud9 • sudo ./cloud9/bin/cloud9.sh 13年4月18日木曜日
  • 25.
    cloud9 IDEの起動 • ./bin/cloud9.sh -w {workspace_dir} で起動 • http://localhost:3131 にアクセス 13年4月18日木曜日
  • 26.
    認証方式 • SSL + Basic認証 + sha1(solt) 13年4月18日木曜日
  • 27.
    restify.authorizationParser • server.use(restify.authorizationParser()); • req.authorization オブジェクト { scheme: <Basic|Signature|...>, credentials: <Undecoded value of header>, basic: { username: $user password: $password } } 13年4月18日木曜日
  • 28.
    認証基盤の実装 server.use(function authenticate(req, res, next) { if (!req.authorization.basic) { res.header('WWW-Authenticate', 'Basic realm=""'); res.send(401); return next(false); } User.findOne({email:req.username}, function (err, user) { if (err) { return next(err); } if (!user && user.password !== req.authorization.basic.password) { return next(new restify.NotAuthorizedError()); } return next(); }); }); 13年4月18日木曜日
  • 29.
    実装上の注意 • I/O系関数は処理順序を保証しない var user = User.findOne({email:req.params.email}); user.password = req.params.password; user.save(); res.send(user); 13年4月18日木曜日
  • 30.
    実装上の注意 • 必ずコールバックにて処理 User.findOne({email:req.params.email}, function(err, user) { user.password = req.params.password; user.save(function(arr, data)) { res.send(data); }); } 13年4月18日木曜日
  • 31.
    実装上の注意 • 複雑な処理の同期が必要な場合は外部 モジュールを検討する ‣ async.js ‣ node-fibers ‣ flow-js ‣ step 13年4月18日木曜日
  • 32.
    実装して思ったこと • 非同期処理が不要な場合node.jsは向かない  (client, middlewareにロジックを置く場合は有) • MongoDBと簡易JsonAPIは相性がいい • node.jsと関連モジュールはまだまだ過渡期 13年4月18日木曜日