跳至主要内容

從零開始的JSON Server

TL;DR

在前端開發的時候,通常最缺的資源就是沒有好的設計稿,另外一個就是想實作的功能沒有後端的支援做不出來。 這時候就該JSON Server上場啦!!

參考資料

相關連結

安裝JSON Server

使用npm安裝,可以安裝在當下的專案中,也可以安裝在global。以下示範安裝在global:

npm install -g json-server

如果要檢查是否有安裝成功,可以在terminal上輸入json-server -v,觀看是否有回傳當前版本資訊 另外也可以從npm list -g去觀看global現在有安裝了哪些套件

/Users/zhangkaiwei-m2/.nvm/versions/node/v18.18.0/lib
├── corepack@0.19.0
├── eslint@8.52.0
├── json-server@0.17.4
├── nodemon@3.0.1
└── npm@9.8.1

創建db.json

可以先進到目前的專案資料夾中,創建一個db.json。這個檔案就是後面會存放所有資料的資料庫。
db.json中,我們需要照著JSON Server的格式去撰寫內容,才有辦法使用JSON Server去開啟。
這邊我們根據官方文件內所提供的資料格式創建內容如下:

db.json
{
"posts": [
{ "id": 1, "title": "json-server怎麼用", "author": "小威" },
{ "id": 2, "title": "vue從開始到放棄", "author": "小威" }
],
"comments": [
{ "id": 1, "body": "小明的留言", "postId": 2 },
{ "id": 2, "body": "小美的留言", "postId": 1 },
{ "id": 3, "body": "小威的留言", "postId": 2 }
],
"profile": [{ "name": "typicode" }]
}

額外新增的幾筆內容是為了後續說明queyr以及透過id get資料用的

開啟伺服器

接著就可以準備開啟伺服器了。這邊需要注意路徑是否正確,否則會讀取不到db.json檔案。
輸入以下指令:

json-server --watch db.json

就可以開啟伺服器啦~ 伺服器預設會開在port:3000,所以我們可以使用http://localhost:3000去戳我們的伺服器。

不想使用port3000開啟JSON Server?

因為在撰寫docusaurus筆記的時候,預設也是開啟port 3000。同一個port號不能同時被兩個應用程式給開啟!
這時候就可以手動指定JSON Server我們希望開啟的port號啦!!可以使用以下指令去指定開啟在port 3002的位置

json-server --watch db.json --port 3002

打開http://localhost:3002可以看到

Congrats!
You're successfully running JSON Server
✧*。٩(ˊᗜˋ*)و✧*。

Resources
/posts 2x
/comments 3x
/profile object
To access and modify resources, you can use any HTTP method:

GET POST PUT PATCH DELETE OPTIONS

undefined
Documentation
README

db.json格式介紹

上述建立的db.json用文字來敘述代表:

  1. posts內有兩篇貼文,並且會紀錄標題以及作者
  2. comments內存放所有留言,每個留言有自己的唯一編號(id),每則留言內會有內容以及紀錄是哪篇貼文下的留言(postID)
  3. profile 這個是JSON Server預設提供的,就沒有額外去更動它

Restful API

所有JSON Server提供的API如下

GET    /posts
GET /posts/1
POST /posts
PUT /posts/1
PATCH /posts/1
DELETE /posts/1

GET method

GET所有資料

在開啟伺服器之後,以前面範例來說,我們可以使用瀏覽器開啟 http://localhost:3002/posts。可以透過瀏覽器看到目前我們db.json 內posts的所有資料

GET特定id的資料

在JSON Server中,id會是唯一值,在post的時候JSON Server也會自動補上。(如果id值重複的時候,JSON Server會回傳http status code:500)
也因為id是唯一值,我們可以在get的時候帶上id去取得特定id值的內容:http://localhost:3002/posts/2

透過上面範例可以看到,瀏覽器只有取得id:2的相關資料

{
"id": 2,
"title": "vue從開始到放棄",
"author": "小威"
}

使用query去取得特定資料

我們可以使用?去使用query,取得特定的資料。例如今天我們輸入http://localhost:3002/comments?body=小美的留言
可以單獨取出comments內的小美的留言,而不會取出其他筆資料

[
{
"id": 2,
"body": "小美的留言",
"postId": 1
}
]
使用id以及使用query取資料

這邊可以觀察到上面的範例,使用query去取資料的時候會返回一個陣列,裡面才是有符合query的內容 相對的直接使用id的GET則會直接返回物件 這邊可能需要多加測試看看有沒有例外狀況

模糊查詢

上面使用query去取得特定資料的時候,有可能我們不知道實際的完整內容。
如果我們這時候使用http://localhost:3002/comments?body=小美
因為實際上comments內並沒有body內容只有"小美"的內容,所以這邊會返回空陣列[]
這時候就可以使用模糊查詢!

我們可以再使用query查詢屬性的時候,在後面加上一個_like,就會啟用模糊查詢的功能 上述範例可以改寫如下:http://localhost:3002/comments?body_like=小美
回傳的結果就可以正確抓取到留言內容(body)包含小美的留言(comments)

[
{
"id": 2,
"body": "小美的留言",
"postId": 1
}
]

POST method

JSON Server支援使用POST去新增資料。官方文件內寫道是使用lowdb去更新資料
並且在POST的時候,需要額外夾帶header,去指定Content-Type: application/json

可以使用Postman去設定傳送的內容(body)以及Content-Type傳送的內容(body)需要使用物件的方式包裹:

body
{
"name": "Peter",
"age": "30"
}

當然,路由的部分也需要指定正確,HTTP request method也要指定是POST
上述的物件我們指定到 http://localhost:3002/profile,並選擇POST方法,就可以正確的新增一筆資料。

為什麼POST無法新增新一筆資料

照著官方提供的資料格式來看,profile原本應該只有一筆資訊,所以並沒有像是posts或是comments用陣列包起來。
並且從命名上也看得出來,profile是單數,並沒有加上s通常建議如果是複數的資源都要在後面加上s

如果像是profile這種單一內容(沒有使用陣列包裹)的話,使用post只會覆蓋掉裡面的那筆資訊,而不會新增下一筆。 所以我們需要先將他用陣列包起來。

另外這邊還有一個點需要注意,因為在使用post的時候會需要根據id值去自動生成下一個唯一的值。
所以profile這邊也需要在一開始的資訊內補上一個 "id":0,接著POST的新內容就會自動補上"id":2 一般來說我們是不用特地去補上id值的,因為JSON Server會自動幫我們判定並補上。如果我們自行添加id值的話也有可能會有重複的問題,這時候就會回傳錯誤資訊。

id值的規則

JSON Server自動補上id值的規則,經過測試是

  1. 會先看目前資料內id值最大的內容
    如果前面profileid我們設定為0,新增的一筆資料就會自動添加"id":1的資訊。
  2. 中間有跳號也不會自動填上。
    也就是說假設我們今天程式自動添加了一筆id值為100,就算一開始只有一筆"id":1下一筆POST所產生的id會是101而不是2

DELETE method

刪除資料的方式如同前面Restful API所介紹的,是使用ID去指定要刪除的資料是哪一筆。使用的方法也很簡單,就是指定哪個資料庫內的id資訊,並且將HTTP request method設定為DELETE即可。這邊可以很簡單的使用postman去驗證。 例如url:http://localhost:3002/profile/2並且設定method:DELETE,就可以刪除profile資料集內id為2的資料。

只能使用id嗎?

先說結論:要刪除資料只能利用id去指定。

前面有介紹到可以使用query或是模糊查詢,如果使用query去指定特定的某筆資料,就算該筆資料搜尋的結果只會返回一筆,也沒有辦法將該url使用DELETE方法去刪除該筆資料
同理,模糊查詢等等的方式也無法刪除。多筆資料的部分更是如此。

所以要刪除多筆資訊的時候感覺好像比較麻煩一些...可能要再找看看有沒有比較好的方式。

PUT method

PUT是用來取代原本內容的,指定路徑的方式跟DELETE相同。在使用PUT的時候,同樣也需要附帶一個header,夾帶Content-Type: application/json的資訊。

舉例來說,原始資料如下:

part of db.json
{
"name":"tom",
"age":30,
"id":7
}

假設今天透過PUT*(url:http://localhost:3002/profile/7)*傳送了以下資料:

BODY(PUT method)
{
"name":"Mary"
}

那麼原始資料會更動如下:

part of db.json
{
"name":"Mary"
"id":7
}

如果再透過PUT*(url:http://localhost:3002/profile/7)*傳送了以下資料:

BODY(PUT method)
{
"time":"2023/11/04"
}

原始資料會更動如下:

part of db.json
{
"time":"2023/11/04",
"id":7
}

結論:PUT的資料除了id以外,其他內容會完全覆蓋掉

那如果在PUT內更新了id呢

經過測試,如果在PUT body內傳送了id的資料,那會直接被JSON Server給無視,但是其他的內容一樣會正常更動。
這部分也很合理,透過了id去指定資源,如果可以透過API將id給更改,那就會有漏洞產生

PATCH method

PATCH是用來更新內容的。與POST以及PUT相同,都需要附帶一個header,夾帶Content-Type: application/json的資訊。 與PUT不同的是,如果今天傳送了原本沒有的資訊,則會在該筆資料內新增新的屬性去儲存我們所傳送的內容(body)。當傳送了原本舊有的屬性時,則會更新該屬性的值(value)

舉例來說,原始資料如下:

part of db.json
{
"name":"tom",
"age":30,
"id":7
}

透過PATCH*(url:http://localhost:3002/profile/7)*傳送了以下資料後:

BODY(PATCH method)
{
"property":"新屬性"
}

原始資料會修改成:

part of db.json
{
"name":"tom",
"age":30,
"property":"新屬性",
"id":7
}

如果再次透過PATCH*(url:http://localhost:3002/profile/7)*傳送以下資料:

BODY(PATCH method)
{
"name":"JIM"
}

則資料會如下,僅有name屬性有變更:

part of db.json
{
"name":"JIM",
"age":30,
"property":"新屬性",
"id":7
}