json-server with express CRUD and User Register/Login

# JSON-SERVER とExpressでCRUDシステムとUSER認証登録を作ってみた❗️

完成したソースコードは、こちらから確認できます。

https://github.com/iwasaki-hub/json-server-with-express

# ①まず、json-serverをlocalhost:3000ポートに設置します。

```terminal:terminal
    npm init -y
    npm i json-server
```
ディレクトリに、**db.json**ファイルを作成します。
```json:db.json
    {
      "posts": [
        { "id": 1, "title": "json-server", "author": "typicode" }
      ],
      "comments": [
        { "id": 1, "body": "some comment", "postId": 1 }
      ],
      "profile": { "name": "typicode" }
    }

```
ターミナルに下記のコマンドを実行すると、json-serverが**localhost:3000**ポートで起動します。
```terminal:terminal
    json-server --watch db.json --port 3000
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/654025/7f671a4c-7eb9-1116-e8d4-f154cd5f7ac2.png)

これで、json-serverの準備完了です。下記のリンクが公式ドキュメントです。

https://www.npmjs.com/package/json-server

# ②Express でサーバーを**localhost:5000**ポートに設置します。
必要なモジュールをインストールします。
```terminal:terminal
    npm install express cors dotenv
    npm install -D nodemon
```
ディレクトリに、**server.js**w作成して下記のコードを記述します。
```javascript:server.js
    require("dotenv").config();
    const express = require("express");
    const app = express();
    const cors = require("cors");
    const PORT = process.env.PORT || 5001;
    
    app.use(express.json());
    app.use(express.urlencoded({ extended: true }));
    app.use(cors());
    
    app.get("/", (req, res) => {
      res.send("Hello Express!! 🚀");
    });
    
    app.listen(PORT, () => {
      console.log(`🪐 Server is running on port http://localhost:${PORT}`);
    });
```
ターミナルで、実行コマンドを入力します。
```terminal:terminal
    nodemon server.js
```
これで、Expressで作成したWebサーバーの完成です。ここから、CRUDシステムを記述していきます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/654025/1e846d09-7951-4dde-3e3e-aafcc3224bf5.png)

現在のディレクトリの構造は、下記の通りです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/654025/39dc5d03-aadf-a6d0-0f81-b3e0a055ad47.png)

# fetch()を使ってjson-serverにデータの追加、削除を行います。
```javascript:server.js
    require("dotenv").config();
    const express = require("express");
    const app = express();
    const cors = require("cors");
    const PORT = process.env.PORT || 5001;
    
    app.use(express.json());
    app.use(express.urlencoded({ extended: true }));
    app.use(cors());
    
    // Get all posts
    app.get("/", async (req, res) => {
      try {
        const response = await fetch("http://localhost:3000/posts");
        const data = await response.json();
        res.status(200).json(data);
      } catch (error) {
        res.status(400).json(error);
      }
    });
    
    // Create new post
    app.post("/", async (req, res) => {
      try {
        const { title, author } = req.body;
        const response = await fetch("http://localhost:3000/posts", {
          method: "POST",
          headers: {
            "Content-type": "application/json",
          },
          body: JSON.stringify({
            title,
            author,
            createdAt: new Date(),
          }),
        });
        const data = await response.json();
        res.status(200).json(data);
      } catch (error) {
        res.status(400).json(error);
      }
    });
    
    // Get single post
    app.get("/:id", async (req, res) => {
      try {
        const id = req.params.id;
        const response = await fetch(`http://localhost:3000/posts/${id}`);
        const data = await response.json();
        res.status(200).json(data);
      } catch (error) {
        res.status(400).json(error);
      }
    });
    
    // Update a post
    app.put("/:id", async (req, res) => {
      try {
        const id = req.params.id;
        const { title, author } = req.body;
        // Check post
        const response = await fetch(`http://localhost:3000/posts/${id}`);
        if (!response.ok) {
          return res.status(400).json({ message: "Post is not exist" });
        }
    
        const data = await response.json();
    
        const updateResponse = await fetch(`http://localhost:3000/posts/${id}`, {
          method: "PUT",
          headers: {
            "Content-type": "application/json",
          },
          body: JSON.stringify({
            ...data,
            title,
            author,
            updatedAt: new Date(),
          }),
        });
    
        const updateData = await updateResponse.json();
        res.status(200).json(updateData);
      } catch (error) {
        res.status(400).json(error);
      }
    });
    
    // Delete a post
    app.delete("/:id", async (req, res) => {
      const id = req.params.id;
      try {
        const response = await fetch(`http://localhost:3000/posts/${id}`, {
          method: "DELETE",
        });
        const data = await response.json();
        res.status(200).json({ message: "Delete a Post" });
      } catch (error) {
        res.status(400).json(error);
      }
    });
    
    app.listen(PORT, () => {
      console.log(`🪐 Server is running on port http://localhost:${PORT}`);
    });
```

今回は、vsCodeにThunder Clientをインストールして、そのからGET、POST、PUT、DELETEリクエストを送ります。

https://www.thunderclient.com/

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/654025/ab194a57-8383-7dfc-b280-d8f590ed1dcc.png)

動作確認をしてみます。

>GET:  http://localhost:5000  Get all Posts
POST: http://localhost:5000 Create new Post
Get:  http://localhost:5000/id Get a single Post
PUT:  http://localhost:5000/id Update a Post
DELETE: http://localhost:5000/id Delete a Post

![2023-03-28_14h44_26.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/654025/b86d2874-743c-4dbc-00f4-be0b65e5201f.gif)

CURD システム完成です！

# 次にユーザーの認証登録を作っていきます。
次のコードを追記します。
```javascript:server.js
// User 認証登録

// Register User

    // 追加のモジュール
    const bcrypt = require("bcrypt");
    const jwt = require("jsonwebtoken");


    app.post("/users/register", async (req, res) => {
      const { email, password } = req.body;
    
      try {
        // Check input all fields
        if (!email || !password) {
          return res.status(400).json({ message: "Please add all fields" });
        }
    
        // Check if user exists
        const usersExist = await fetch("http://localhost:3000/users");
        const users = await usersExist.json();
        const user = users.find((user) => user.email === email);
    
        if (user) {
          return res.status(400).json({ message: "User is already exists" });
        }
    
        // Hash Password
        const hashedPassword = await bcrypt.hash(password, 10);
    
        // create JWT
        const token = jwt.sign({ email }, process.env.ACCESS_TOKEN_SELECT, {
          expiresIn: "24h",
        });
    
        // Create User
        const response = await fetch("http:localhost:3000/users", {
          method: "POST",
          headers: {
            "Content-type": "application/json",
          },
          body: JSON.stringify({
            email,
            password: hashedPassword,
            token,
            createdAt: new Date(),
          }),
        });
    
        const newUser = await response.json();
    
        res.cookie("token", token, { httpOnly: true });
    
        res.status(200).json({ message: "New User created", newUser });
      } catch (error) {
        res.status(400).json(error);
      }
    });
    
    // Login user
    app.post("/users/login", async (req, res) => {
      try {
        const { email, password } = req.body;
        if (!email || !password) {
          return res.status(400).json({ message: "Please add all fields" });
        }
        const usersExist = await fetch("http://localhost:3000/users");
        const users = await usersExist.json();
    
        const user = users.find((user) => user.email === email);
    
        if (!user) {
          return res.status(200).json({ message: "User is not exist" });
        }
    
        const isMatch = await bcrypt.compare(password, user.password);
    
        if (!isMatch) {
          return res.status(200).json({ message: "Password is not match" });
        }
    
        const token = jwt.sign({ email }, process.env.ACCESS_TOKEN_SELECT, {
          expiresIn: "24h",
        });
    
        res.status(200).json({
          message: "User is LogIn Successfully",
          ...user,
          newToken: token,
        });
      } catch (error) {
        res.status(400).json(error);
      }
    });
```

動作確認をしてみます。

![2023-03-29_15h23_31.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/654025/a04a5525-ea96-2906-9878-24155178cd66.gif)

>POST: http://localhost:5000/users/register  Register user
POST: http://localhost:5000/usrs/login Login user

# 最後に
今回は、ローカル環境にjson-serverを設置してCRUDシステムとUser認証を実装しましたが、次のステップとしてFirebaseやMongoDBを使ってオンラインのデーターベースを利用すると、さらに理解が深まるとおもいます。

https://firebase.google.com/?hl=ja

![2023-03-29_15h31_32.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/654025/19fd3431-a7fd-62ea-6d36-2feb29ce2434.gif)

https://www.mongodb.com/ja-jp

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/654025/66de7c45-5ed5-9748-635e-eaf513fbedf8.png)