フロントエンドエンジニアの Node / Express 入門 ~  CRUDなAPIを作る ~

# 初めに

フロントエンドエンジニアになって7ヶ月が経ちました。最近サーバーにも興味が出てきたのでフロントと相性の良さそうな Node.js と、そのフレームワークの Express を使ってAPIを作ってみます。あと MongoDBとかいう子が Node/Express のプロジェクトでよく使われるっぽいので一緒に入門してみます。
サーバー初心者なのでその辺はあしからず、、

## 本記事で取り扱うこと

* Node.js / Express を用いたAPI作成しPostmanで動作確認をする
* MVCモデルっぽく作ってみる
* MongoDBとの接続

## 扱わないこと

* DB設計とかの踏み込んだ部分
* Typescript との連携
* Vue / React などのフレームワークとの繋ぎ込み
* EJSなどのテンプレートエンジン

## バージョン

Node 
Express 
Mongoose

## ディレクトリ構成

```
node-project
  ├── controllers
  │    └── controller.js
  ├── models
  │    └── model.js
  ├── routes
  │    └── route.js
  ├── node_modules
  ├── .env
  ├── app.js
  ├── package.json
  └── package-lock.json
```

# プロジェクトの作成からサーバーの立ち上げまで

この項目でやること

- プロジェクトの作成
- 必要なパッケージのインストール
- 5000番ポートでのサーバーの立ち上げ
## ディレクトリ作成
フォルダを作りましょう
フォルダ名は任意のもので大丈夫です。

```
mkdir node-project
cd node-project
```
## package.json作成
```
npm init -y
```
package.json が自動作成される

ちなみに -y をつけることでデフォルトの設定になりますが、つけないと自分好みにカスタマイズできるらしいです（[参考サイト](https://www.webprofessional.jp/10-npm-tips-and-tricks/#:~:text=てください。-,5)）

## 必要なパッケージのインストール

今回は下記3つのパッケージをインストールしていきます。
- express
- mongoose
- dotenv
- nodemon

```
npm i express mongoose dotenv nodemon 
```

## mongooseとは
- MongoDBを操作するためのnpmモジュール。
- MongoDB用モデリングツールで、Node.jsの非同期環境でうまく動作することを目的として設計されています。
- モデルを定義して操作することで、MongoDBのコレクション／ドキュメントを操作できます

公式サイト: https://mongoosejs.com/
## dotenv

- 環境変数を定義するときに使う
- mongoDBのURLとかgitにあげたらやばいので、、

参考サイト
- https://www.npmjs.com/package/dotenv
- https://maku77.github.io/nodejs/env/dotenv.html

## nodemon
デフォルトの設定だとファイルを書き換えるたびに `node app.js`としないと更新されない。さすがにそれは面倒なので「ファイルが更新されるたびに自動的にサーバーを再起動してくれるツール」というのがnodemonです。

参考サイト: https://qiita.com/mitsuhiro_K/items/429ca479b4e191bfea4d

## 環境変数の設定

.envファイルを作成し、ポート番号とMongoDBを設定しましょう。
```
PORT = 5000
DB_URL = MongoDBのURL
```
MongoDBの設定方法はこちらをご参照ください、、
https://reffect.co.jp/node-js/mongodb-cloud#MongoDB_Cloud

## app.js の作成
プロジェクト直下に app.js を作成し、インストールしたパッケージを読み込みましょう。
app.js は サーバーのスターティングポイントになります

```js
// 必要なモジュールのインポート
require("dotenv").config();
const express = require("express");
const mongoose = require("mongoose");

const app = express(); // express のインスタンス化
const port = process.env.PORT || 5000; // ポート番号の指定

// MongoDB接続
mongoose
  .connect(process.env.DB_URL)
  .then(() => console.log('データベース接続に成功しました'))
  .catch((err) => console.log(err));

app.listen(port, () => console.log(`server running at localhost:${port}`)); // サーバー立ち上げ
```

## サーバーを起動してみる

ここまで出来たらサーバーの立ち上げてみましょう。
先ほどインストールした nodemon を使って サーバーを起動できるように package.json を書き換えましょう。

**package.json**
```
"scripts": {
　　"dev": "nodemon app.js"
},
```

npmスクリプトを上記のように書き換えることで以下コマンドで立ち上げることが出来ます。!
```
npm run dev
```

localhost:5000 にアクセスすると以下スクショのような表示になっており、コンソールに

* server running at localhost:5000
* データベース接続に成功しました！

が出ていれば一旦大丈夫です！
（エラーが出てるのは一旦気にしないでください）

![スクリーンショット 2022-02-26 9.51.43.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1741616/924b110a-393e-e4a3-8d9e-4ba0e31ca0b3.png)

# Controller と Route の設定

この項目では以下のことを行います

* Controller の作成
* Route の設定
* 設定したAPIのエンドポイントを叩く
※DBとの接続はまだ行いません！！

## Route の設定

routesというディレクトリを作成し、route.js を作成しましょう。
以下のような構文で記述することができます。

```
router.HTTPメソッド("エンドポイント", 実行する処理);
```

CRUD な処理を全て記述しておきましょう。

**routes/route.js**
```js
const express = require('express');
const router = express.Router();
const API = require('../controllers/controller.js');

router.get('/', API.fetchAllPost); // 全件取得
router.get('/:id', API.fetchPostDetail); // 詳細取得
router.post('/', API.createPost); // 投稿
router.patch('/:id', API.updatePost); // 更新
router.delete('/:id', API.deletePost); // 削除

module.exports = router;
```

## Controller を作成

先ほどルーティングを設定しましたが現状だとそれに対応するメソッドが存在しません。Controller を作成し、それぞれのエンドポイントに対応するメソッドを定義していきましょう。

**controllers/controller.js**

```js

module.exports = class API {
  static async fetchAllPost(req, res) {
    try {
      res.send('全件取得に成功!!');
    } catch (err) {
      res.status(404).json({ message: err.message });
    }
  }
  static async fetchPostDetail(req, res) {
    try {
      res.send('詳細取得に成功!!');
    } catch (err) {
      res.status(404).json({ message: err.message });
    }
  }
  static async createPost(req, res) {
    try {
      res.send('投稿に成功!!');
    } catch (err) {
      res.status(404).json({ message: err.message });
    }
  }
  static async updatePost(req, res) {
    try {
      res.send('更新に成功!!');
    } catch (err) {
      res.status(404).json({ message: err.message });
    }
  }
  static async deletePost(req, res) {
    try {
      res.send('削除に成功!!');
    } catch (err) {
      res.status(404).json({ message: err.message });
    }
  }
};

```

Controller を app.js に追記します。

app.js
```js
const posts = require('./routes/route');
app.use('/api/posts', posts);
```
参考: https://expressjs.com/ja/guide/routing.html

## Postman で動作確認

ここまできたら一度Postman で指定したエンドポイントにアクセスしてみましょう。

`http://localhost:5000/api/posts`
`http://localhost:5000/api/${id}`

※現時点では ${id} は適当な数字を入れても処理は走ります。
![スクリーンショット 2022-02-26 12.13.05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1741616/c796a87e-8122-45fc-e641-b28036a78a64.png)

# Modelを作成してMongoDBと接続する

先ほど作成した Controller を Model と接続していきましょう。
models でスキーマを定義していきます。
1つの投稿が持ってるプロパティと、型を定義していく。

## models の作成

**models/model.js**

```js
const mongoose = require('mongoose');

const PostSchema = mongoose.Schema({
  title: { type: String, required: true },
  content: { type: String, required: true },
  created_at: { type: Date, default: Date.now },
});

// スキーマからPostモデルを生成する
module.exports = mongoose.model('Post', PostSchema);
```

参考 
https://mongoosejs.com/docs/guide.html
https://mongoosejs.com/docs/models.html

## Controllers と Models を繋ぎ込む

先ほど作成した Model を Controller で呼び出しましょう。
スキーマから生成したモデルが持つメソッドがあるので、対応するメソッドを呼び出します。
例えば新しくPOSTを作成するときは、 `create()` というメソッドがあるので呼び出してみましょう。

**controllers/controller.js**
```js
const Post = require('../models/model'); // 追記

module.exports = class API {
  // 省略
  static async createPost(req, res) {
    try {
      const post = req.body;
      try {
        await Post.create(post);
        res.status(201).json({ message: '投稿に成功!!' });
      } catch (err) {
        res.status(400).json({ message: err.message });
      }
    } catch (err) {
      res.status(404).json({ message: err.message });
    }
  }
  // 省略
};
```

Postman でPOSTの処理を確認してみましょう。
![スクリーンショット 2022-02-26 17.57.38.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1741616/3ac3d8f2-45e5-4725-3edf-6f88d41492f6.png)
無事に投稿できました！!
MongoDBの中にも保存されていますね。

![スクリーンショット 2022-02-26 18.00.40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1741616/f86c5bc1-7117-5b88-d4c5-f325b6975461.png)

他の処理も Mongoose の公式ドキュメントを参考に、それぞれに対応するメソッドに書き換えていきます。
公式: https://mongoosejs.com/docs/api.html#Model

**controllers/controller.js**
```js
static async fetchAllPost(req, res) {
    try {
      const posts = await Post.find();
      res.status(200).json(posts);
    } catch (err) {
      res.status(404).json({ message: err.message });
    }
  }
  static async fetchPostDetail(req, res) {
    const id = req.params.id;
    try {
      const post = await Post.findById(id);
      res.status(200).json(post);
    } catch (err) {
      res.status(404).json({ message: err.message });
    }
  }
  static async createPost(req, res) {
    // さっき書いたから省略
  }
  static async updatePost(req, res) {
    const id = req.params.id;
    const newPost = req.body;
    try {
      await Post.findByIdAndUpdate(id, newPost);
      res.status(201).json({ message: 'post updated' });
    } catch (err) {
      res.status(400).json({ message: err.message });
    }
  }
  static async deletePost(req, res) {
    const id = req.params.id;
    try {
      const result = await Post.findByIdAndDelete(id);
      res.status(201).json({ message: 'post deleted' });
    } catch (err) {
      res.status(400).json({ message: err.message });
    }
  }
```

※ ちなみに `/${id}` にあたる部分は投稿時に自動生成される id をのことです

他の処理も書き換えて見て Postman で動作確認して見ましょう。

# 最後に

長々と書いてしまいましたが以上になります！！
業務ではフロントにしか触れてないのでサーバー側の技術は新鮮でした。
Javascript で記述できる点や、 MongoDBが直感的に記述できたのでよかったです。 

# 参考サイト

https://mongoosejs.com/
https://www.npmjs.com/package/dotenv
https://qiita.com/mitsuhiro_K/items/429ca479b4e191bfea4d
https://maku77.github.io/nodejs/env/dotenv.html
https://mongoosejs.com/docs/guide.html
https://mongoosejs.com/docs/models.html