---
# System prepended metadata

title: '[GDSC] NodeJS (2)'
tags: [GDSC]

---

# [GDSC] NodeJS (2)

[TOC]

> 我不是 Javascript 專業，講錯歡迎指教

---

## HTTP Request Method (REST)

#### Protocol
- HTTP 
- REST / RESTful

#### Dependency
- `npm install mongodb mongoose dotenv`
- Create database, open [Atlas](https://cloud.mongodb.com/v2#/org/6589537fa4947020c782e44b/)
    1. build a project (free plan) with sample data 
    2. choose `Drivers` option to build connection
    3. connection link should be like 
        `mongodb+srv://<username>:<password>@cluster0.c0ikn.mongodb.net/?retryWrites=true&w=majority&appName=Cluster0`
    4. Click `Collection` column, a sample database named `sample_mflix`
    5. Replace connection link with your username and password, add database name `sample_mflix` behind the base link **(or use `dbName` option with mongoose)**
    6. mainly use `user` table in this course


#### Load .env
File path: `app.js`
```javascript=
require('dotenv').config();
const dbLink = process.env.MONGODB_API;
```

#### Connect to database
File path: `app.js`
```javascript=
// Add dependency
const mongoose = require('mongoose');

// Connect to ODM with driver link
mongoose.connect(dbLink, {
  dbName: "sample_mflix",
});

// Event listener
const db = mongoose.connection;
db.on('error', console.error.bind(console, 'MongoDB connection error:'));
db.once('open', function () {
  console.log('Connected to MongoDB');
});
```

#### Schema
File path: `models/user.js`

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

const userSchema = new mongoose.Schema({
    name: { type: String, required: true },
    email: { type: String, required: true, unique: true },
    password: { type: String, required: true },
});

// syntax: mongoose.model(modelName, schema)
const User = mongoose.model('User', userSchema);

module.exports = User;
```
:question: 為什麼知道 schema 長這樣呢? 我又不會通靈
:a: 答案是去 Atlas 看已經建立好的 sample data 樣貌！

**API**
[`mongoose.Schema()`](https://mongoosejs.com/docs/api/mongoose.html#Mongoose.prototype.Schema())
[`mongoose.model()`](https://mongoosejs.com/docs/api/mongoose.html#Mongoose.prototype.model())

### GET

**用途**
從伺服器 (server) 取得特定資源

**範例**
File path: `routes/users.js`
Route: `/users`
```javascript=
router.get('/', async (req, res) => {
    try {
        const users = await User.find();
        res.json(users);
    } catch (error) {
        res.status(500).json({ message: error.message });
    }
});
```

File path: `routes/users.js`
Route: `/users/:id`
```javascript=
router.get('/:id', async (req, res) => {
    try {
        const user = await User.findById(req.params.id);
        if (!user) {
            return res.status(404).json({ message: 'User not found' });
        }
        res.json(user);
    } catch (error) {
        res.status(500).json({ message: error.message });
    }
});
```

**API**
[`Model.find()`](https://mongoosejs.com/docs/api/model.html#Model.find())

### POST

**用途**
將數據發送到伺服器

**範例**
File path: `routes/users.js`
Route: `/users`
```javascript=
router.post('/', async (req, res) => {
    try {
        const user = new User(req.body);
        await user.save();
        res.status(201).json(user);
    } catch (error) {
        res.status(400).json({ message: error.message });
    }
});
```

**API**
[`Model.prototype.save()`](https://mongoosejs.com/docs/api/model.html#Model.prototype.save())

### PUT 

**用途**
更新伺服器中的指定數據，通常是全部更新
> 如果是部分更新，使用 `PATCH`

**範例**
File path: `routes/users.js`
Route: `/users/:id`
```javascript=
router.put('/:id', async (req, res) => {
    try {
        const user = await User.findByIdAndUpdate(req.params.id, req.body, { new: true });
        if (!user) {
            return res.status(404).json({ message: 'User not found' });
        }
        res.json(user);
    } catch (error) {
        res.status(400).json({ message: error.message });
    }
});
```

**API**
[`Model.findByIdAndUpdate()`](https://mongoosejs.com/docs/api/model.html#Model.findByIdAndUpdate())

### DELETE 

**用途**
刪除伺服器中的指定數據

**範例**
File path: `routes/users.js`
Route: `/users/:id`
```javascript=
router.delete('/:id', async (req, res) => {
    try {
        const user = await User.findByIdAndDelete(req.params.id);
        if (!user) {
            return res.status(404).json({ message: 'User not found' });
        }
        res.json({ message: 'User deleted' });
    } catch (error) {
        res.status(500).json({ message: error.message });
    }
});
```

**API**
[`Model.findByIdAndDelete()`](https://mongoosejs.com/docs/api/model.html#Model.findByIdAndDelete())

### OPTIONS

**用途**
設定請求資訊, including mehtod, headers, status code...

**範例**
```javascript=
app.options('/users', (req, res) => {
  res.setHeader('Allow', 'GET, POST, PUT, DELETE, OPTIONS');
  res.status(204).send();
});
```

**API**
GeeksforGeeks - [Node response.setHeader() Method](https://www.geeksforgeeks.org/node-js-response-setheader-method/)

> [!Note] More details
> - MDN - [HTTP request methods](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods)
> - MDN - [HTTP response status codes](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status)

### Manual Testing

#### Prerequisite
- Install vscode plugin **Thunder Client**

---

## Websocket 

1. 使用 `express-generator` 生成專案後，在 `/bin/www` 設定

    ```javascript=
    /**
     * Import websocket package
     */
    const WebSocket = require('ws');

    /**
     * Create a websocket instance
     */
    const wss = new WebSocket.Server({ server });

    wss.on('connection', function connection(ws) {
        console.log('A new client connected');

        ws.on('message', function incoming(message) {
            console.log('received: %s', message);
            ws.send(`Server received: ${message}`);
        });

        ws.on('close', () => {
            console.log('Client disconnected');
        });

        ws.send('Welcome to the WebSocket server!');
    });
    ```
2. Add a file named `ws-client.html` under `/public/we-client.html`

    ```html=
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>WebSocket Client</title>
    </head>
    <body>
        <h1>WebSocket Client</h1>
        <input id="message" type="text" placeholder="Type a message">
        <button onclick="sendMessage()">Send</button>
        <div id="messages"></div>

        <script>
            const ws = new WebSocket(`ws://${window.location.host}`);

            ws.onopen = function() {
                document.getElementById('messages').innerHTML += '<p>Connected to the server</p>';
            };

            ws.onmessage = function(event) {
                document.getElementById('messages').innerHTML += `<p>Server: ${event.data}</p>`;
            };

            ws.onclose = function() {
                document.getElementById('messages').innerHTML += '<p>Disconnected from the server</p>';
            };

            function sendMessage() {
                const message = document.getElementById('message').value;
                ws.send(message);
                document.getElementById('messages').innerHTML += `<p>You: ${message}</p>`;
            }
        </script>
    </body>
    </html>
    ```
3. Access `http://localhost:3000/ws-client.html`