###### 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. 系統會自動產生範例檔案 ![](https://i.imgur.com/NHmyu6G.png) ![](https://i.imgur.com/vSEeMQ0.png) ### *step3.* 成功畫面 - 開啟`http://localhost:3000/`顯示以下畫面表示成功。 ![](https://i.imgur.com/KAAkmjd.png) > 預設首頁 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`的資料所以系統會找不到對應的資料。