#06 No-code 之旅 — 用 Next.js 解决前後端?API Routes 简介

连假最後一天!分享了一些关於 Next.js 这个框架,虽然他是 React 的框架,可是你们知道吗?Next.js 也可以同时实作後端喔!没错,前後端全包~ 怎麽做呢?今天来看看 Next.js 的 API Routes,看完应该会有想法怎麽实作,不过看这篇文章之前,可以先看看上一篇关於 Next.js 的 routing 喔!

Next.js API Routes

API Routes

Next.js 的 API routes 也采用 file-based routing,意思是每个档案代表一个 route,而档案名称 (file name) 会变成 route 的 path name (路径名称)。Next.js 会把 pages/api 资料夹 / 目录里面的所有档案看成 route (路径) 的一部分:

  • pages/api/index.js 代表 / API route
  • pages/api/user.js 代表 /user API route

API routes 里的所有程序码是属於後端的部分,所以只会跑在服务器端。这代表我们写的 API 不会让我们前端的 bundle size 变大,不会影响网页的下载时间!也是因为是跑在服务器端,我们可以实作各种後端的功能,例如操作资料库~

每个 pages/api 里的档案需要 export 一个 default function (函式) 作为 API 的 request handler (处理请求的函式),然後每个 handler 会有两个 parameters:

Dynamic (动态) API routes

像 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 API routes

每个 index 档案代表根目录,所以

  • pages/api/posts.js 代表 /api/posts API route
  • pages/api/posts/index.js 也代表 /api/posts API route

注意:pages/api/posts/[pid].js 并不代表 /api/posts 只代表 /api/posts/1/api/posts/2

Catch all API routes

属於 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/cquery 会是:

{ "slug": ["a", "b", "c"] }

Optional catch all routes

跟 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/postsquery 物件会是空物件

{ } // GET `/api/post` (空物件)
{ "slug": ["a"] } // `GET /api/post/a`
{ "slug": ["a", "b"] } // `GET /api/post/a/b`

API Middlewares

Next.js 有提供内建的 middlewares 塞进去 req 物件里,除了内建的之外,我们还可以用其他 Connect / Express 相容的 middlewares。

Built-in (内建) 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 可以看这里喔~

Connect / Express 相容的 middlewares

想要在我们的 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

官方 docs 在这里

Response Helper

虽然 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 可以是 stringobject,或是 Buffer
  • res.redirect([status,] path):导页,把浏览器导到别 path 或 URL,status 是 HTTP status code,是个非必须的 parameter,预设为 307 (Temporary redirect).

小结

我们看完 API routes 之後,可以开始做一些全端专案了(误
大家会想用 API routes 做哪些事呢?有了这个真的让前後端开发简单一些~
祝大家明天上班上课愉快!

晚安 <3

看更多


<<:  #7 JavaScript Modules

>>:  Day6 Redis组态档设定-SNAPSHOT

Day20-名称宇宙? 什麽是Namespace

前一章我们提到了daemonset 建立出来的pod,不在deafult的Namespaces内,所...

[Day13] [笔记]React Hooks-UseReducer

简介 useReducer 简单来说就是 useState 进阶用法。而且写法上其实跟 Redux ...

Day8. 依点成形,创造物件 - RigidBody(下)

今天是陪了我们两天的刚体的最後一集,是不是开始有点想念它了呢? 那没关系,好好珍惜我们今天跟刚体相处...

# [Day17] 建立订单交易API_11

本节将接续上节,调整了一下request message中的ExpireDate 正确的respon...

Day7 if else实作

今天我们会实作两支程序,第一支程序要做让使用者输入两个数,若这两个数相起来是100,则显示a,是20...