Node.js(Express)からmongoDBをREST API的にたたく2019

# やりたいこと

mongoDBを使って、**Key=Value**の１ペアを格納して、**Key**で値がとれて、**Key**でUpdateもDeleteもできる。
ただそれだけのシンプルなDBがほしいから作りました。

> _id ではなく Key というのがみそです。

例えば、**name=ドルリーレーン** とやればそれをただ記録してくれる。**nameは？** といえば**ドルリーレーン**と返してくれる。**othername=エラリークイーン** とやれば追加で記録してくれて、**othernameは？** といえば**エラリークイーン**と返してくれる。

本当に簡単な実験的な仕組みをつくりたいときにこういうシンプルなDBがあると便利なので作りました。

※ 認証などの仕組みはいっさいないのでローカルのテストとか閉じたネットワークでお試しください
※ Keyは何個でも格納できますが同じKeyが複数あってもエラーにもなりません。いいんです実験用なので。


> ついでに expree + mondgoDB の良いサンプルにもなると思いますので公開しておきます

## 使い方はこうです

```console
$ curl http://xxx/data -X POST -d "name=mio"  ← データ挿入
$ curl http://xxx/data/name  ← mio が返ってくる
$ curl http://xxx/data/  ← 格納している全データが返ってくる
$ curl http://xxx/data/name -X PUT -d "name=miomio"  ← nameの値が更新される
$ curl http://xxx/data/name -X DELETE  ← nameの値が削除される
```

## 環境＋コード含めて解説

### mongoDBのインストール！

EC2 Amazon Linuxでやっていますが他の環境では適宜読み替えを。

```sh
sudo vi /etc/yum.repos.d/mongodb-org-4.0.repo
```
```ini
[mongodb-org-4.0]
name=MongoDB Repository
baseurl=https://repo.mongodb.org/yum/amazon/2013.03/mongodb-org/4.0/x86_64/
gpgcheck=1
enabled=1
gpgkey=https://www.mongodb.org/static/pgp/server-4.0.asc
```

```sh
sudo yum install -y mongodb-org
```

確認。

```sh
mongo -version
```

OKですね。次に進みます。

### Expressのサーバ立ち上げ

Node.jsは入ってること前提で。

```sh
npm install -g express-generator
express -e site
cd site
npm install
PORT=8080 npm start
```

> 個人的な色々な理由でポート8080で立ち上げてますがこの辺りはご自由に。。

とりあえずブラウザでアクセスしてみて確認。良く見慣れたこれが出ます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/33095/a095f60a-b41d-f798-337e-5418e4435f19.png)

OKです。
Ctrl+Cとかでサーバは止めておきます。

### ExpressからmongoDBの操作のアプリを作る。

Expressのフォルダ（今回は`site`）にいる状態て `mongodb` を扱うためのライブラリをインストール。

```sh
npm install mongodb
```

REST APIの本題である`data.js`を作ります。
※コードが冗長ですみません…

```javascript:routes/data.js
ar express = require('express');
var router = express.Router();

var MongoClient = require('mongodb').MongoClient;
var ObjectID = require('mongodb').ObjectID;
var url = "mongodb://localhost:27017/";

// GET find
router.get( '/', function ( req, res ) {
  MongoClient.connect(url, { useNewUrlParser: true }, function(err, client) {
    client.db('db').collection("restapi").find().toArray( function(err, r) {
      client.close();
      res.send(r);
    });
  });
} );

// GET find :id
router.get( '/:id', function ( req, res ) {
  MongoClient.connect(url, { useNewUrlParser: true }, function(err, client) {
    let key = {}
    key[req.params.id] = { $regex:".*" }
    client.db('db').collection("restapi").findOne( key, function(err, r) {
      client.close();
      res.send(r);
    });
  });
} );

// POST insert data
router.post( '/', function ( req, res ) {
  MongoClient.connect(url, { useNewUrlParser: true }, function(err, client) {
    let obj = req.body;
    client.db('db').collection("restapi").insertOne(obj , function(err, r) {
      client.close();
      res.send(r);
    });
  });
});

// PUT update data
router.put( '/:id', function ( req, res ) {
  MongoClient.connect(url, { useNewUrlParser: true }, function(err, client) {
    let obj = req.body;
    let key = {}
    key[req.params.id] = { $regex:".*" }
    client.db('db').collection("restapi").findOneAndUpdate( key, {$set:obj}, {}, function(err, r) {
      client.close();
      res.send(r);
    });
  });
} );

// DELETE remove data
router.delete( '/:id', function ( req, res ) {
  MongoClient.connect(url, { useNewUrlParser: true }, function(err, client) {
    let key = {}
    key[req.params.id] = { $regex:".*" }
    client.db('db').collection("restapi").findOneAndDelete( key, function(err, r) {
      client.close();
      res.send(r);
    });
  });
} );

module.exports = router;
```

ここで作った`data.js`を使うようにメインの`app.js`も少しだけ書き換えます。

```javascript:app.js
var createError = require('http-errors');
var express = require('express');
var path = require('path');
var cookieParser = require('cookie-parser');
var logger = require('morgan');

var indexRouter = require('./routes/index');
var dataRouter = require('./routes/data'); // ★これを追加★

var app = express();

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

app.use(logger('dev'));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));

app.use('/', indexRouter);
app.use('/data', dataRouter); // ★これを追加★

// catch 404 and forward to error handler
app.use(function(req, res, next) {
  next(createError(404));
});

// 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;
```

※ 最初にはいってた Users は消しちゃってますが残っててもいいです

### テスト

これで動く環境は整ったので実行。

```ini
PORT=8080 npm start
```

クライアント側のシェルなどから適当にコマンドをうってみます。

```console
$ curl http://xxxx:8080/data
[]
```

最初は空です。
データを2つくらい追加してみます。

```console
$ curl http://xxxx:8080/data -X POST -d "name=mio"
{"result":{"n":1,"ok":1},"connection":{"id":3,"host":"localhost","port":27017},"ops":[{"name":"mio","_id":"5cfa406159b3531cf05214f6"}],"insertedCount":1,"insertedId":"5cfa406159b3531cf05214f6","n":1,"ok":1}

$ curl http://xxxx:8080/data -X POST -d "milk=coffee"
{"result":{"n":1,"ok":1},"connection":{"id":5,"host":"localhost","port":27017},"ops":[{"milk":"coffee","_id":"5cfa40b459b3531cf05214f7"}],"insertedCount":1,"insertedId":"5cfa40b459b3531cf05214f7","n":1,"ok":1}
```

追加されたっぽいので確認してみます。

```console
$ curl http://xxxx:8080/data/name
{"_id":"5cfa406159b3531cf05214f6","name":"mio"}

$ curl http://xxxx:8080/data/milk
{"_id":"5cfa40b459b3531cf05214f7","milk":"coffee"}
```

更新してみます。

```console
$ curl http://xxxx:8080/data/name -X PUT -d "name=miomio"

$ curl http://xxxx:8080/data/name
{"_id":"5cfa406159b3531cf05214f6","name":"miomio"}
```

削除も。

```console
$ curl http://xxxx:8080/data/name -X DELETE
```

とりあえずこんな感じ。

あとはこれを Vue.js などのフロントからgetでもpostでも何でもうまいこと扱ってあげたら色々簡単にためすにはよいと思います。

### 謝辞

こちらのQiitaからインスピレーションいただきました。多謝。
https://qiita.com/itagakishintaro/items/a1519998a91061cbfb1e