連假最後一天!分享了一些關於 Next.js 這個框架,雖然他是 React 的框架,可是你們知道嗎?Next.js 也可以同時實作後端喔!沒錯,前後端全包~ 怎麼做呢?今天來看看 Next.js 的 API Routes,看完應該會有想法怎麼實作,不過看這篇文章之前,可以先看看上一篇關於 Next.js 的 routing 喔!
Next.js 的 API routes 也採用 file-based routing,意思是每個檔案代表一個 route,而檔案名稱 (file name) 會變成 route 的 path name (路徑名稱)。Next.js 會把 pages/api
資料夾 / 目錄裡面的所有檔案看成 route (路徑) 的一部分:
pages/api/index.js
代表 /
API routepages/api/user.js
代表 /user
API routeAPI routes 裡的所有程式碼是屬於後端的部分,所以只會跑在伺服器端。這代表我們寫的 API 不會讓我們前端的 bundle size 變大,不會影響網頁的下載時間!也是因為是跑在伺服器端,我們可以實作各種後端的功能,例如操作資料庫~
每個 pages/api
裡的檔案需要 export 一個 default function (函式) 作為 API 的 request handler (處理請求的函式),然後每個 handler 會有兩個 parameters:
req
:request 物件,是一種 http.IncomingMessage,還有包含一些內建的 middlewares
res
:response 物件,是個 http.ServerResponse,加上一些 helper functions
像 Next.js 的 routing 一樣,API routes 也支援 dynamic (動態) routing。如果我們對 pages/api/posts/[pid]/comments/[cid].js
路徑發個 /api/posts/world/comments/hello
的請求:
export default function handler(req, res) {
const { pid, cid } = req.query
res.end(`Comment #${cid} for post #${pid}`)
}
上面的 API 會回傳 Comment #hello for post #world
。
每個 index 檔案代表根目錄,所以
pages/api/posts.js
代表 /api/posts
API routepages/api/posts/index.js
也代表 /api/posts
API route注意:
pages/api/posts/[pid].js
並不代表/api/posts
只代表/api/posts/1
,/api/posts/2
等
屬於 dynamic API routes 的一種,不過 catch all 的意思是不需要一個一個定義動態的部分,把所有動態的資訊抓起來,讓 route 比較彈性,寫法跟其餘參數(rest parameter)一樣。
pages/api/posts/[...slug].js
可以代表 /api/posts/a
也可以是 /api/posts/a/b
,/api/posts/a/b/c
等slug
可以是 param
或是其他東西都可以,這些動態資訊也會出現在 query
物件裡面,例如 /api/posts/a/b/c
的 query
會是:
{ "slug": ["a", "b", "c"] }
跟 pages 的 optional catch all routes 一樣,只要把 catch all 的 [...slug]
改成 [[...slug]]
就會變成 optional catch all。意思是 slug
這個 array (陣列) 可以是不存在的,
pages/posts/[[...slug]].js
可以代表 /posts/a
也可以是 /posts/a/b
,/posts/a/b/c
等,不過也可以代表 /posts
所以當我們發請求到 /api/posts
,query
物件會是空物件
{ } // GET `/api/post` (空物件)
{ "slug": ["a"] } // `GET /api/post/a`
{ "slug": ["a", "b"] } // `GET /api/post/a/b`
Next.js 有提供內建的 middlewares 塞進去 req
物件裡,除了內建的之外,我們還可以用其他 Connect / Express 相容的 middlewares。
req.cookies
:請求的 cookies,預設為 {}
req.query
:請求的 query string,預設為 {}
req.body
:已經被 parse 的請求 body,會根據 content-type
做 parse,當沒有 body 時會回傳 null
。如果不想要 body 被 parse,可以在 API route 檔案裡加 config
:
export const config = {
api: {
bodyParser: false,
},
}
除了 body parser 的 config 以外,還有更多 API 相關的 config 可以看這裡喔~
想要在我們的 API routes 增加其他 middlewares 也可以!直接在 handler function 裡執行該 middleware 再繼續跑 API 本身的 code,像:
import Cors from 'cors'
// initialize cors middleware
const cors = Cors({
methods: ['GET', 'HEAD'],
})
// helper method 去執行 middleware,結束後才繼續跑下去
// 發生錯誤時跳 error
function runMiddleware(req, res, fn) {
return new Promise((resolve, reject) => {
// fn 為 middleware
fn(req, res, (result) => {
if (result instanceof Error) {
return reject(result)
}
return resolve(result)
})
})
}
async function handler(req, res) {
// 先跑 middleware
await runMiddleware(req, res, cors)
// API 本身的邏輯
res.json({ message: 'Hello Everyone!' })
}
export default handler
雖然 API routes 不是 Express.js,可是 Next.js 有提供一些類似的 helper functions 來幫助我們開發而提升 DX (開發者體驗):
res.status(code)
:設定 response 的 status code (要符合 HTTP status code)res.json(body)
:回傳 JSON 為 response 給瀏覽器 (客戶端)res.send(body)
:回傳 HTTP response,body 可以是 string
,object
,或是 Buffer
res.redirect([status,] path)
:導頁,把瀏覽器導到別 path 或 URL,status
是 HTTP status code,是個非必須的 parameter,預設為 307
(Temporary redirect
).我們看完 API routes 之後,可以開始做一些全端專案了(誤
大家會想用 API routes 做哪些事呢?有了這個真的讓前後端開發簡單一些~
祝大家明天上班上課愉快!
晚安 <3