###### tags: `Angular` `JSON Server`
# 🌝[T]JSON Server
[Github-json-server](https://github.com/typicode/json-server)
## 前言
> 在30秒內不打任何程式碼 快速建立一個RESTful API
`JSON Server`是套可以快速簡單設計出`RESTful API`的套件,在前後端分工卻又不能同步開發的窘境下,可以幫助前端快速建立假的API先行開發。
除了可以解決不同步開發的問題外,也可以依此撰寫測試程式,在串接後端資料時減少錯誤,也能在發生錯誤時,加速排查速度。
**JSON Server 優缺點.**
優點
- 簡易上手、快速。
- 處理CRUD類的問題非常好用。
缺點
- API完成後需要調整串接的API。
- 較複雜的API需要寫後端的CODE。
**開發前注意事項.**
1. 必須要有API資料的 interface/type。
- 若是在沒有的情況下製作,可能會與實際上的落差太大導致做白工。
**參考資料.**
[Fake Your Angular Backend Until You Make It](https://blog.angulartraining.com/fake-your-angular-backend-until-you-make-it-8d145f713e14)
[進擊的前端工程師:今天就自己打造API吧!](https://www.slideshare.net/WillHuangTW/use-json-server-as-a-fake-rest-api)
[使用 JSON Server 快速模擬 Restful API](https://andy6804tw.github.io/2018/02/01/json-server-intro/)
> 關鍵字: `Fake RESTful API` `Mock Data`
## 注意事項
- 使用`POST`、`PUT`、`PATCH`、`DELETE` Request Method時,將會直接變更db.json的資料。
- 事先備份原始資料
- 預設支援CORS問題與JSONP協定(對所有來源開放)。
- 早期瀏覽器不支援CORS,是用JSONP來解決跨Domain Request問題。
- JSONP Protocol就是在路由後接上`?callbacl=[funcName]`,funcName要存在網頁中。
```
// myfunc要是網頁中的Function Name
localhost:3000/post?callback=myfunc
```
網頁(測試的專案)會去load 上面的網址,然後當成一個javascript載入,javascript載入是用Type = script載入 所以不會有跨Domain的問題,所以一定可以載進來,缺點就是只能接受GET Request。
- 發出`POST`、`PUT`、`PATCH`請求時,標頭應包含`Content-Type: application/json`且內文使用JSON格式,否則它將返回2XX狀態代碼,但不對數據進行變更。
- ID值不可修改。`id`在`PUT`、`PATCH`請求中會被忽略,只有在`id`值是尚未被使用(沒重複的id)的情況下`POST`請求時會被使用。
- 變更預設`id`屬性的名稱
```
json-server --id _id
```
## 環境設定
### *step1.* 安裝套件
- 使用npm在全域環境中安裝`json-erver`套件
```npm
npm install -g json-server
```
### *step2.* 啟動服務
- 以下兩種方式都可以用來啟動JSON Server。
```npm
json-server --watch db.json
```
```npm
json-server db.json
```
- 情境一
1. 建立`db.json`檔案
2. 終端機內輸入`json-server --watch db.json` / `json-server db.json`
- 情境二
1. 直接在終端機內輸入`json-server --watch db.json` / `json-server db.json`
2. 系統會自動產生範例檔案


### *step3.* 成功畫面
- 開啟`http://localhost:3000/`顯示以下畫面表示成功。

> 預設首頁 http://localhost:3000/
### *tips1.* npm 腳本
- 若想要新增npm腳本,可以在`package.json` `scripts` 內新增腳本,這樣就可以在終端機使用`npm run api`。
```json
"api": 'json-server db.json'
```
## 實際操作
[Stackblitz- JSON-server Demo](https://stackblitz.com/edit/json-server-demo?file=src/app/app.component.html)
:::info
💡**小補充**
要使用`HttpClient`功能前,記得要在`**.module.ts`內先匯入模組`HttpClientModule`。
:::
### GET 取得資料
==db.json==
```json
{
"get": [
{
id: 0,
title: 'T0',
}
]
}
```
==http.component.ts==
```typescript
constructor(
private httpClient: HttpClient,
){}
onGet() {
const url = 'httpL//localhost:3000/get';
// const url = 'httpL//localhost:3000/get/0';
this.httpClient.get(`${url}`).subscribe(x => console.log(x));
}
```
==http.html==
```htmlembedded
<button (click)="onGet()">GET</button>
```
==回傳資料==
```json
{
id: 0,
title: 'T0'
}
```
### POST 建立資料
若是沒有給id值,系統會自動遞增新增。
==db.json==
```json
{
"post": [
{
id: 0,
title: 'T0',
}
]
}
```
==http.component.ts==
```typescript
constructor(
private httpClient: HttpClient,
){}
onPost() {
const url = 'httpL//localhost:3000/post';
const data = {
id: 1,
title: 'T1',
}
this.httpClient.post(`url`, data).subscribe(x => console.log(x));
}
```
==http.html==
```htmlembedded
<button (click)="onPost()">POST</button>
```
==回傳資料==
```json
{
id: 0,
title: 'T0'
},
{
id: 1,
title: 'T1'
}
```
### PUT 更新資料(ALL)
使用`PUT`要注意,沒修改的資料也要一起送出,否則會被蓋掉。
==db.json==
```json
{
"put": [
{
id: 0,
title: 'T0',
memo: 'M0'
}
]
}
```
==http.component.ts==
```typescript
constructor(
private httpClient: HttpClient,
){}
onPut() {
const url = 'httpL//localhost:3000/put';
const data = {
id: 0,
title: 'T1',
memo: 'M0'
}
this.httpClient.put(`url/${data.id}`, data).subscribe(x => console.log(x));
}
```
==http.html==
```htmlembedded
<button (click)="onPut()">PUT</button>
```
==回傳資料==
```json
{
id: 0,
title: 'T1',
memo: 'M0'
}
```
### PATCH 更新資料(PART)
`PATCH`可以只修改部分資料,所以只要將有修改的資料送出就行了。
==db.json==
```json
{
"patch": [
{
id: 0,
title: 'T0',
memo: 'M0'
}
]
}
```
==http.component.ts==
```typescript
constructor(
private httpClient: HttpClient,
){}
onPatch() {
const url = 'httpL//localhost:3000/patch';
const data = {
id: 0,
title: 'T1',
}
this.httpClient.patch(`url/${data.id}`, data).subscribe(x => console.log(x));
}
```
==http.html==
```htmlembedded
<button (click)="onPatch()">PATCH</button>
```
==回傳資料==
```json
{
id: 0,
title: 'T1',
memo: 'M0'
}
```
### DETELE 刪除資料
==db.json==
```json
{
"delete": [
{
id: 0,
title: 'T0',
}
]
}
```
==http.component.ts==
```typescript
constructor(
private httpClient: HttpClient,
){}
onDelete() {
const url = 'httpL//localhost:3000/delete/0';
this.httpClient.delete(`${url}`).subscribe(x => console.log(x));
}
```
==http.html==
```htmlembedded
<button (click)="onDelete()">DELETE</button>
```
==回傳資料==
```json
{}
```
## 建立大量資料
[JSON GENERATOR](https://www.json-generator.com/)
### *step1.* 建立js檔
==db.js==
```javascript=
module.exports = () => {
const data = { users: [] }
// Create 1000 users
for (let i = 0; i < 1000; i++) {
data.users.push({ id: i, name: `user${i}` })
}
return data
}
```
### *step2.* 執行
```npm
json-server db.js
```
## 資料操作方式
### Paginate 資料分頁
- `_page=[number]`,將資料分成[number]頁
```
http://localhost:3000/books?_page=3
```
- `_limit=[number]`,限制資料一頁有[number]筆 (預設是10筆)
```
http://localhost:3000/books?_limit=20
```
- 指令可以混用,只要在中間加上`&`即可。
```
http://localhost:3000/books?_page=3&_limit=20
```
### Sort 資料排序
- `_sort=[columnName]`&`_order=[asc/desc]`(預設升冪asc)
```
http://localhost:3000/books?_sort=name&_order=desc
```
- 多個欄位排序,用`,`隔開
```
http://localhost:3000/books?_sort=name,memo&_order=desc,asc
```
### Slice 資料切片
- `_start=[numberS]`,第[numberS]筆開始 / `_end=[numberE]`,第[numberE]筆結束
```
http://localhost:3000/books?_start=20&_end=30
```
- 也可以與`_limit`搭配使用
```
http://localhost:3000/books?_start=20&_limit=10
```
### Operators 篩選運算子
- `_like=[value]`,相似[value] (字串)
```
http://localhost:3000/books?name_like=王
```
- `_gte=[number]`,大於等於[number] (數值)
```
http://localhost:3000/books?price_gte=199
```
- `_lte=[number]`,小於等於[number] (數值)
```
http://localhost:3000/books?price_lte=199
```
- `_ne=[number]`,不等於[number] (數值)
```
http://localhost:3000/books?price_ne=199
```
### Search 全文檢索
- `?q=[value]`,查詢資料符合[value]的值
```
http://localhost:3000/get?q=王小明
```
### Route 自訂網址路由
#### step1. 建立路由定義檔
```npm
routes.json
```
#### step2. 定義路由
==routes.json==
```json
{
"/book/:name": "/get?name_like=:name&",
"/book/:name/:price/": "/get?name_like=:name&price_lte=:price"
}
```
#### step3. 啟動路由
```npm
json-server db.json --routes routes routes.json
```
#### step4. 使用自訂路由
```
輸入此自訂路由
http://localhost:3000/book/王
等同輸入
http://localhost:3000/get?name_like=王
```
```
輸入此自訂路由
http://localhost:3000/book/王/199
等同輸入
http://localhost:3000/get?name_like=王&price_lte=199
```
## 網頁伺服器
- 建立public 資料夾,並將靜態檔案放入即可
```
預設首頁檔名: index.html
```
- Angular CLI `ng build` 後出來的dist也可以
```npm
json-server --watch db.json --static dist
```
- 指定顯示網頁的PORT
```npm
json-server --watch db.json --port 3001
```
## 錯誤集
### ⚡[file] doesn't seem to exist
#### 錯誤訊息
`⚡Oops, db.json doesn't seem to exist
Creating db.json with some default data`
#### 發生情境
我在`./src`新增資料夾api,並新增`db.json`在`./src/api`內,接著使用`json-server api/db.json`想啟動服務,就發生此錯誤。
#### 解決方法
如果發生這個問題,建議可以讓系統自動產生範例檔案。
```npm
json-server --watch db.json
```
#### 錯誤原因
這個錯誤就是==檔案位置不對/沒有這個檔案==,簡單說就是它找不到你指定的檔案。
它的目錄是由專案內開始算起,所以要使用`json-server api/db.json`,要在與`src`資料夾同一層新增api資料夾。
### ⚡Status Code 404
#### 錯誤訊息
`⚡無`
#### 發生情境
資料如下,使用GET /members/1 時,回傳的狀態代碼卻是404。
==db.json==
```json
{
"members": [
{
"ID": 0,
"Name": "王小明"
}
]
}
```
#### 解決方法
==*方法一*== 將`db.json`的ID改成id
==*方法二*== 在終端機輸入`json-server --id ID`
#### 錯誤原因
JSON server預設的ID值為`id`,我的資料內並沒有`id`的資料所以系統會找不到對應的資料。