LoginSignup
3

More than 5 years have passed since last update.

Node.js + Express + あえてCloudant で REST APIを作成する(開発編②)

Last updated at Posted at 2017-04-06

はじめに

Node.js + Express + あえてCloudant で REST APIを作成する(開発編①)の続きです。
CRUDを行うサンプルコードを開発してローカル環境とBluemix環境で実行します。

CRUDのサンプルコード

  • メイン・スクリプトを編集:
    • ここでcloudantClient.jsを読み込み、/animalsとパスを切って, cloudantClient.jsを実行させます。
    • パスの指定方法がREST API設計では重要です。
app.js
var express = require('express');
var path = require('path');
var favicon = require('serve-favicon');
var logger = require('morgan');
var cookieParser = require('cookie-parser');
var bodyParser = require('body-parser');

var routes = require('./routes/index');
// 今回追加部分
var cloudantClient = require('./routes/cloudantClient');

var app = express();

// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'e');

// uncomment after placing your favicon in /public
//app.use(favicon(path.join(__dirname, 'public', 'favicon.ico')));
app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));

app.use('/', routes);
// 今回追加部分
app.use('/animals',cloudantClient);

// catch 404 and forward to error handler
app.use(function(req, res, next) {
  var err = new Error('Not Found');
  err.status = 404;
  next(err);
});

// error handler
app.use(function(err, req, res, next) {
  // set locals, only providing error in development
  res.locals.message = err.message;
  res.locals.error = req.app.get('env') === 'development' ? err : {};

  // render the error page
  res.status(err.status || 500);
  res.render('error');
});

module.exports = app;

  • 今回開発したコード全体:
cloudantClient.js

require('dotenv').load();

var express = require('express');
var router = express.Router();

// Cloudantの認証とサンプルのanimaldbの指定
var Cloudant = require('cloudant');
var me = process.env.cloudant_username; // Set this in .env file
var password = process.env.cloudant_password; // Set this in .env file
var cloudant = Cloudant({account:me, password:password}); 
var db = cloudant.db.use("animaldb");

// GETでドキュメント参照とエラーハンドリング
router.get('/:_id', function(req, res, next){
    db.get(req.params._id, function(err, data){
        if(!err){
            res.set('X-RateLimit-Limit','180');
            res.status(200).json({"document":data});
            //res.status(200).send(JSON.stringify(data));
            console.log(data);
        }
        else if(err.statusCode == 404){
            console.log("Error:", err.statusCode);
            res.set('X-RateLimit-Limit','180');
            res.status(404).json({"error":{"code":404,"message":"Resource not found, Specify the correct id","info":"https://docs.cloudant.com/http.html#http-status-codes"}});
        }
        else if(err.statusCode == 500){
            console.log("Error:", err.statusCode);
            res.set('X-RateLimit-Limit','180');
            res.status(500).json({"error":{"code":500,"message":"Internal Server Error,Please retry later","info":"https://docs.cloudant.com/http.html#http-status-codes"}});
        }
        else{
            console.log("Error:", err);
            res.set('X-RateLimit-Limit','180');
            res.status(err.statusCode).json({"error":{"code":err.statusCode,"message":"Other error","info":"https://docs.cloudant.com/http.html#http-status-codes"}});
        }
    });
});

//POSTでドキュメント作成とエラーハンドリング
router.post('/', function(req, res, next) {
  db.insert(req.body, req.body._id, function(err, data){
        if(!err){
            var id = req.body._id;
            var url = './animals/'+ id;
            res.set('X-RateLimit-Limit','180');
            res.status(201).json({"created":data,"url":url});
        //res.status(201).send(JSON.stringify(data));
            console.log(data);
        }
        else if(err.statusCode == 409){
            console.log("Error:", err.statusCode);
            res.set('X-RateLimit-Limit','180');
            res.status(409).json({"error":{"code":409, "message":"Resource already exists","info":"https://docs.cloudant.com/http.html#http-status-codes"}});
        }
        else if(err.statusCode == 500){
            console.log("Error:", err.statusCode);
            res.set('X-RateLimit-Limit','180');
            res.status(500).json({"error":{"code":500,"message":"Internal Server Error,Please retry later","info":"https://docs.cloudant.com/http.html#http-status-codes"}});
        }
        else{
            console.log("Error:", err);
            res.set('X-RateLimit-Limit','180');
            res.status(err.statusCode).json({"error":{"code":err.statusCode,"message":"Other error","info":"https://docs.cloudant.com/http.html#http-status-codes"}});
        }
 });
});

//PUTでドキュメント更新とエラーハンドリング
router.put('/:_id', function(req, res, next){
  db.insert(req.body, function(err, data){
        if(!err){
            res.set('X-RateLimit-Limit','180');
            res.status(200).json({"updated":data});
            //res.status(200).send(JSON.stringify(data));
            console.log(data);
        }
        else if(err.statusCode == 409){
            console.log("Error:", err.statusCode);
            res.set('X-RateLimit-Limit','180');
            res.status(409).json({"error":{"code":409,"message":"Update failed, specify the correct id and rev","info":"https://docs.cloudant.com/http.html#http-status-codes"}});
        }
        else if(err.statusCode == 500){
            console.log("Error:", err.statusCode);
            res.set('X-RateLimit-Limit','180');
            res.status(500).json({"error":{"code":500,"message":"Internal Server Error,Please retry later","info":"https://docs.cloudant.com/http.html#http-status-codes"}});
        }
        else{
            console.log("Error:", err);
            res.set('X-RateLimit-Limit','180');
            res.status(err.statusCode).json({"error":{"code":err.statusCode,"message":"Other error","info":"https://docs.cloudant.com/http.html#http-status-codes"}});
        }
  });
});

// DELETEでドキュメント削除とエラーハンドリング
router.delete('/:_id/:_rev', function(req, res, next){
  db.destroy(req.params._id,req.params._rev, function(err, data){
        if(!err){
            res.set('X-RateLimit-Limit','180');
            res.json({"deleted":data});
            res.status(204);
        console.log(data);
        }
        else if(err.statusCode == 404){
            console.log("Error:", err.statusCode);
            res.set('X-RateLimit-Limit','180');
            res.status(404).json({"error":{"code":404,"message":"Resource not found, Specify the correct id and rev","info":"https://docs.cloudant.com/http.html#http-status-codes"}});
        }
        else if(err.statusCode == 409){
            console.log("Error:", err.statusCode);
            res.set('X-RateLimit-Limit','180');
            res.status(409).json({"error":{"code":409,"message":"Delete failed, specify the correct id and rev","info":"https://docs.cloudant.com/http.html#http-status-codes"}});
        }
        else if(err.statusCode == 500){
            console.log("Error:", err.statusCode);
            res.set('X-RateLimit-Limit','180');
            res.status(500).json({"error":{"code":500,"message":"Internal Server Error,Please retry later","info":"https://docs.cloudant.com/http.html#http-status-codes"}});
        }
        else{
            console.log("Error:", err);
            res.set('X-RateLimit-Limit','180');
            res.status(err.statusCode).json({"error":{"code":err.statusCode,"message":"Other error","info":"https://docs.cloudant.com/http.html#http-status-codes"}});
        }
  });
});

module.exports = router;

動かしてみる

以下のコマンドでapp.jsを実行します。

$ npm start

Kobito.blvkgP.png

Chrome Postmanからアクセス:作成

  • URLは/animals以降は指定せず、ボディに以下のJSONを入れ、application/Jsonを指定し、POSTリクエスト送信&結果: Kobito.kw02he.png

Chrome Postmanからアクセス:参照

  • URLは/animals/catを指定し、上記で作成したCatドキュメントにGETリクエスト送信&結果: Kobito.37Znnj.png

Chrome Postmanからアクセス:更新

  • URLは/animals/catを指定し、ボディに以下のJSONを入れ、application/Jsonを指定し、PUTリクエスト送信&結果:
  • POSTで作成したときの違いはボディに_revが入っていることです。あとは各項目好きなところを更新します。
  • レスポンス・ボディで_rev番号が更新されていることがわかります。 Kobito.91QozQ.png

Chrome Postmanからアクセス:削除

  • ボディは指定せず、URLに_idと_revを指定し、DELETEリクエスト送信&結果: Kobito.BREENW.png

Bluemixにデプロイしてみる

$ cd AppName // アプリのディレクトリに移動
$ cf push AppName // BluemixにCLIでログインしてあるのが前提です

Bluemix上で開始されていることが確認できます。
Kobito.FNxjPE.png

  • http://<hostname.mybluemix.net>/animals/ に対して、同様にPostmanから各HTTPメソッドを試して問題ないことを確認します。
  • Catドキュメントに対するGETリクエスト送信&結果: Kobito.iZEw3n.png

まとめ

以上で(あえて)Cloudant NoSQL DBのサンプルDBに対してCRUD操作のREST APIをNode.jsで実装してみました。
コードは初級レベルなので、よりよい書き方がありましたらコメントいただけますと幸いです。

つまったところ・注意点など

  • node app.js の起動方法はExpress3、npm start の起動方法はExpress4、といったように、お作法がバージョンで結構違いますが、ネット記事などの情報はExpress3ベースがまだ多いので、検索するときは注意が必要です。
  • Firefox PosterだとJSONをPOSTするときに意図しない改行コードなど入ることがあり、意図したとおりのデータ挿入ができなかったため、Chrome Postmanで実行したところ成功しました。これからはPostmanを使うつもりです。

参考文献

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3