# Lesson 3 - JSON Server

Trong các bài học trước, dữ liệu được khởi tạo ngay trong file JS, sẽ bị reset khi restart lại server. Với json server, thư viện sẽ giúp chúng ta khởi tạo một server với RESTful API để tương tác, lưu trữ dữ liệu trên file json.

Nếu json-server cũng tạo ra được server với chuẩn RESTful API và cũng có file json để lưu trữ, vậy tại sao ta phải học những nội dung dài dòng các buổi trước?

Mục đích của json-server để chúng ta tập luyện với RESTful API, lưu trữ data vào các resource và sử dụng file json để làm điều đó. Với dự án thực tế, ta phải có các nền tảng quản trị cơ sở dữ liệu 'real' để lưu trữ và json-server sẽ giúp ta làm quen.

## Cài đặt Json Server

Để cài đặt Json Server, ta sẽ sử dụng câu lệnh:&#x20;

```
npm install -g json-server
```

{% hint style="info" %}
-g là lệnh để cài đặt phạm vi toàn bộ máy tính
{% endhint %}

<figure><img src="https://content.gitbook.com/content/bApz6rKK01BWUpp5sM0n/blobs/BVMpY5u7qPaIE00JiJkl/image.png" alt=""><figcaption><p>file package.json</p></figcaption></figure>

## File json

Ta cần phải có một file json để lưu trữ data, thực hiện tạo một file db.json để lưu trữ các resource như sau:

`db.json`

```json
{
    "users": [
        {
            "id": "US001",
            "userName": "MindX"
        },
        {
            "id": "US002",
            "useName": "Nobi Nobita"
        }
    ],
    "posts": [
        {
            "id": "PS001",
            "content": "Nội dung học về JSON Server!",
            "authorId": "US001"
        }
    ],
    "comments": [
        {
            "id": "CMT001",
            "postId": "PS001",
            "content": "Bài học này rất ý nghĩa! Cảm ơn MindX!",
            "authorId": "US002"
        }
    ]
}
```

<figure><img src="https://content.gitbook.com/content/bApz6rKK01BWUpp5sM0n/blobs/Cotku7S949FrELxbO5xA/image.png" alt=""><figcaption><p>file db.json</p></figcaption></figure>

Giải thích file db.json với json-server:

Định dạng của json cũng giống như object, nên ta có thể thấy có dấu {} bao bọc toàn bộ các key và value bên trong.

Mỗi một key đầu tiên, sẽ được gọi là một resource:

1. users: danh sách chứa thông tin id và tên của user
2. posts: danh sách các bài post của một user bao gồm: id của bài post, content của bài post, authorId là id của user đã tạo bài post này
3. comments: chưa các comment (bình luận) của các bài post bao gồm: id của comment, postId là id của bài post, content là nội dung của comment, authorId là id của user đã comment.

## Run json-server

Để chạy json-server ta sẽ sử dụng lệnh:

```
json-server --watch db.json
```

<figure><img src="https://content.gitbook.com/content/bApz6rKK01BWUpp5sM0n/blobs/vhVqlxfuGxk6bPONZQUk/image.png" alt=""><figcaption><p>Kết quả chạy</p></figcaption></figure>

Khi chạy thư viện json-server, thư viện sẽ giúp chung ta tạo ra một server database với chuẩn RESTful API, sử dụng file json như một CSDL.

Hãy thử truy cập các đường dẫn hiển thị trong terminal!

## Concurrently

Khi muốn chạy server Express và Json server, ta sẽ phải chạy câu lệnh riêng biệt:

1. node index.js
2. json-server --watch db.json

Để đỡ tốn thời gian, ta sẽ cần thực thi được 2 câu lệnh này đồng thời và nhanh chóng!

Concurrently là một thư viện giúp chúng ta chạy nhiều câu lệnh đồng thời.

Để cài đặt, ta sử dụng câu lệnh:

`npm i concurrently --save-dev`

{% hint style="info" %}
\--save-dev: là lệnh cấu hình rằng thư viện này ta chỉ sử dụng ở trong môi trường dev (develop), khi triển khai trên môi trường production sẽ không cần dùng thư viện này
{% endhint %}

<figure><img src="https://content.gitbook.com/content/bApz6rKK01BWUpp5sM0n/blobs/hb4VEn3as2R6h1pE0zmt/image.png" alt=""><figcaption><p>devDependencies</p></figcaption></figure>

Cài đặt scripts:

<figure><img src="https://content.gitbook.com/content/bApz6rKK01BWUpp5sM0n/blobs/lTY6ZPiXkLsTSrJU6L0y/image.png" alt=""><figcaption><p>package.json</p></figcaption></figure>

{% hint style="info" %}
scripts: là nơi để cấu hình các câu lệnh chạy của npm, khi chạy lệnh bằng cách

`npm run {key}` sẽ chạy lệnh bằng giá trị được truyền

Ví dụ trong trường hợp này:

`npm run dev`

`Lập sẽ chạy câu lệnh là giá trị được truyền`

Để định nghĩa câu lệnh cần sử dụng cặp dấu \\"{lệnh}\\"
{% endhint %}

Tuy nhiên, ta có thể thấy, sẽ rất phiền phức, mỗi khi thay đổi logic, ta cần phải restart lại server, vậy ta sẽ sử dụng một thư viện, sẽ giúp ta restart lại server mỗi khi có sự thay đổi

## Nodemon

Nodemon là một thư viện, hỗ trợ trong quá trình phát triển bằng cách restart lại server mỗi khi có sự thay đổi.

Cài đặt thư viện bằng cách sử dụng câu lệnh

`npm i nodemon --save-dev`

Thay đổi câu lệnh node index.js bằng nodemon index.js và chạy câu lệnh npm run dev

<figure><img src="https://content.gitbook.com/content/bApz6rKK01BWUpp5sM0n/blobs/sZeibPs6HB2wDwcuyesr/image.png" alt=""><figcaption></figcaption></figure>

Như vậy, mỗi khi có sự thay đổi, server của chúng ta sẽ tự động restart mà không cần phải làm bằng tay nữa!

## ExpressJs và Json server

Khởi tạo server với express:

```javascript
import express from 'express';
const app = express();
app.use(express.json());

app.get('', (req, res) => {
    res.send({
        message: 'Hello MindX-er'
    });
});

app.listen(8080, () => {
    console.log('Server is running!');
});
```

Để kết nối được tới json-server, ta cần phải biết, json-server là một server và khi kết nối sẽ mất thời gian. Vậy ta sẽ cần phải sử dụng kiến thức về bất đồng bộ.

Thực hiện viết API kết nối với resource users:

```javascript
app.get('/users', (req, res) => {
    fetch('http://localhost:3000/users').then((rs) => {
        return rs.json()
    }).then((data) => {
        res.send({
            message: 'Hello MindX-er',
            data
        });
    });
});
```

<figure><img src="https://content.gitbook.com/content/bApz6rKK01BWUpp5sM0n/blobs/PZRYLcRbvcovybOsfEiH/image.png" alt=""><figcaption><p>Postman</p></figcaption></figure>

{% hint style="info" %}
Hoàn toàn có thể sử dụng cú pháp của async await để xử lý promise!
{% endhint %}

## Error Handling

Từ các bài học trước, việc ta kiểm tra, truy xét lỗi vẫn còn hạn chế, lỗi không chỉ đến từ dữ liệu, mà còn đến từ nhiều nguyên nhân khác như API không xác định, lỗi từ việc kết nối,... hậu quả sẽ dẫn đến như dữ liệu không được xác thực nhưng vẫn có thể đi vào database, server bị sập và ta sẽ có 1 số cách xử lý và tác dụng như sau:

1. Validation - Xác thực dữ liệu.
2. Middleware - Là một cách validation, nhưng trước hoặc sau handler function tách biệt dùng để ghi lại, xử lý hoặc phản hồi cho người dùng cho các lỗi có thể chủ động nắm bắt được từ lập trình viên. (sẽ được nói trong bài sau).
3. Status code - Là các mã trả về cho client để thông báo dạng lỗi, ...

Trong nội dung này, ta sẽ tìm hiểu và sử dụng với khối lệnh try catch với validation, khối lệnh xử lý ngoại lệ, giúp giảm thiểu tình trạng server bị sập đến từ các lý do khác nữa, không chỉ là từ dữ liệu

Tác dụng: Khối lệnh try catch sẽ giúp thực thi logic trong khối lệnh try và sẽ nhận, xử lý các ngoại lệ trong khối lệnh catch.

Theo dõi cú pháp:

```javascript
try {
    // code logic ...
    if(error) throw new Error("Message error")
} catch (error){
    // code logic ...
}
```

Trong khối lệnh try, bất kỳ một exeption gì, sẽ đều được pass qua catch với cú pháp `throw.` Khi chương trình đọc được throw, lập tức sẽ thực thi logic trong khối lệnh catch và những câu lệnh phía dưới throw sẽ không được thực thi

Theo dõi đoạn code ví dụ phía dưới đây:

```javascript
app.post('/register', (req, res) => {
    try {
        const {userName, email, passWord} = req.body;
        // kiểm tra dữ liệu đầu vào nhận từ body
        if(!userName) throw new Error('userName is required!');
        if(!email) throw new Error('email is required!');
        if(!password) throw new Error('password is required!');
        
        const newUser = users.push({
            userName,
            email,
            passWord
        });
        res.status(201).send({
            data: newUser,
            success: true,
            error: 'Đăng ký tài khoản thành công'
        });
    } catch (error){
        res.status(403).send({
            data: null,
            success: false,
            error: error.message
        });
    }
)
```

Trong đoạn code phía trên, khi kiểm tra các thông tin được gửi lên là rỗng, thì sẽ trả ra lỗi với cú pháp throw new Error và ngay lập tức các câu lệnh phía dưới sẽ không được thực thi, đây cũng là một cách hữu dụng để chúng ta tối giản if else mặc dù sẽ hơi khó hình dung cho người mới bắt đầu.

Tuy nhiên, với các thư viện chúng ta tích hợp, một số sẽ có tích hợp việc throw Error khi xảy ra lỗi, nên với try catch như phía trên, không chỉ giúp handle lỗi về data còn cả về các ngoại lệ của các tác nhân khác.

Việc xử lý, xác thực (validation) dữ liệu đầu vào cũng là một vấn đề khá khó khăn, dài dòng và đôi khi còn phức tạp trong việc xử lý, cũng như ví dụ phía trên, trong trường hợp còn nhiều trường, định dạng cần phải xét nữa, sử dụng logic tự triển khai sẽ mất rất nhiều thời gian. vậy nên ta cũng có thể sử dụng thư viện để giúp cho chúng ta việc xác thực dữ liệu nhanh chóng hơn.

{% hint style="info" %}
Có thể tìm hiểu thêm về thư viện  [**yup**](https://www.npmjs.com/package/yup)**,** tuy nhiên cần phải có kiến thức về middleware trước, ta sẽ được tìm hiểu về middleware trong bài học sau.
{% endhint %}

## Tập luyện

Phía trên, ta đã được làm quen định dạng, các dữ liệu trong file db.json, error handling với khối lệnh try catch, chúng ta sẽ tập luyện với một số bài tập sau:

1. Viết API việc đăng ký user với userName, id sẽ được là một string ngẫu nhiên, không được phép trùng, bắt đầu từ ký tự US (ví dụ: US8823).
2. Viết API cho phép user tạo bài post (thêm bài post, xử lý id tương tự user).
3. Viết API cho phép user chỉnh sửa lại bài post (chỉ user tạo bài viết mới được phép chỉnh sửa).
4. Viết API cho phép user được comment vào bài post
5. Viết API cho phép user chỉnh sửa comment (chỉ user tạo comment mới được sửa)
6. Viết API lấy tất cả comment của một bài post.
7. Viết API lấy tất cả các bài post, 3 comment đầu (dựa theo index) của tất cả user .
8. Viết API lấy một bài post và tất cả comment của bài post đó thông qua postId

{% hint style="info" %}
Tất cả các API đều phải được sử dụng với RESTful API.
{% endhint %}

## Kết

Trên đây, ta đã được tìm hiểu về JSON Server, để tạo ra một dạng mock database với RESTful API. Nhưng mọi thứ sẽ chỉ phục vụ cho mục đích học tập, còn trên môi trường thực tế, ta sẽ không sử dụng JSON Server để làm nơi quản trị database mà sẽ sử dụng các Hệ quản trị CSDL để thực hiện việc lưu trữ dữ liệu và ta sẽ được tìm hiểu vào bài sau!
