四、API Routes

郁子大约 3 分钟约 911 字笔记NextJSCodevolution

(一)API 路由

  • NextJS 是一个完整的堆栈框架
  • 可以在 React 中编写前端代码,也可以编写被前端代码调用的 API
  • API 路由允许创建 RESTful 端点作为 NextJS 项目的文件夹结构的一部分
  • pages/api/* 文件夹下,可以为项目定义各种 API
    • 不需要编写任何额外的服务器代码就可以添加业务逻辑,也不需要配置任何 API 路由
  • NextJS 提供了编写全栈 React+Node 应用程序所需的一切

1.pages/api/index.js

  • 访问 localhost:3000/api
export default function handler(req, res) {
  res.status(200).json({
    name: "Home API route",
  });
}

2.pages/api/dashboard.js

  • 访问 localhost:3000/api/dashboard
export default function handler(req, res) {
  res.status(200).json({
    name: "Dashboard API route",
  });
}

3.pages/api/blog/index.js

  • 访问 localhost:3000/api/blog
export default function handler(req, res) {
  res.status(200).json({
    name: "Blog API route",
  });
}

(二)API GET Request

1.data/comments.js

export const comments = [
  {
    id: 1,
    text: "This is the first comment",
  },
  {
    id: 2,
    text: "This is the second comment",
  },
  {
    id: 3,
    text: "This is the third comment",
  },
];

2.pages/api/comments/index.js

import { comments } from "../../../data/comments";
export default function handler(req, res) {
  res.status(200).json(comments);
}

3.pages/comments/index.js

import { useState } from "react";

function CommentsPage() {
  const [comments, setComments] = useState([]);

  const fetchComments = async () => {
    const response = await fetch("/api/comments");
    const data = await response.json();
    setComments(data);
  };

  return (
    <>
      <button onClick={fetchComments}>Load comments</button>
      {comments.map((comment) => {
        return (
          <div key={comment.id}>
            {comment.id} {comment.text}
          </div>
        );
      })}
    </>
  );
}

export default CommentsPage;

(三)API POST Request

1.pages/api/comments/index.js

import { comments } from "../../../data/comments";

export default function handler(req, res) {
  if (req.method === "GET") {
    res.status(200).json(comments);
  } else if (req.method == "POST") {
    const comment = req.body.comment;
    const newComment = {
      id: Date.now(),
      text: comment,
    };
    comments.push(newComment);
    res.status(201).json(newComment);
  }
}

2.pages/comments/index.js

import { useState } from "react";

function CommentsPage() {
  const [comments, setComments] = useState([]);
  const [comment, setComment] = useState("");

  const fetchComments = async () => {
    const response = await fetch("/api/comments");
    const data = await response.json();
    setComments(data);
  };

  const submitComment = async () => {
    const response = await fetch("/api/comments", {
      method: "POST",
      body: JSON.stringfy({ comment }),
      headers: {
        "Content-Type": "application/json",
      },
    });
    const data = await response.json();
    console.log(data);
  };

  return (
    <>
      <input type="text" value={comment} onChange={(e) => setComment(e.target.value)} />
      <button onClick={submitComment}>Submit comment</button>
      <button onClick={fetchComments}>Load comments</button>
      {comments.map((comment) => {
        return (
          <div key={comment.id}>
            {comment.id} {comment.text}
          </div>
        );
      })}
    </>
  );
}

export default CommentsPage;

(四)API DELETE Request —— Dynamic API Routes

1.pages/api/comments/[commentId].js

  • 访问 localhost:3000/api/comments/2
import { comments } from "../../../data/comments";

export default function handler(req, res) {
  const { commentId } = req.query;

  if (req.method === "GET") {
    const comment = comments.find((comment) => comment.id === parseInt(commentId));
    res.status(200).json(comment);
  } else if (req.method === "DELETE") {
    const deletedComment = comments.find((comment) => comment.id === parseInt(commentId));
    const index = comments.findIndex((comment) => {
      comment.id === parseInt(commentId);
    });
    comments.splice(index, 1);
    res.status(200).json(deletedComment);
  }
}

2.pages/comments/index.js

import { useState } from "react";

function CommentsPage() {
  const [comments, setComments] = useState([]);
  const [comment, setComment] = useState("");

  const fetchComments = async () => {
    const response = await fetch("/api/comments");
    const data = await response.json();
    setComments(data);
  };

  const submitComment = async () => {
    const response = await fetch("/api/comments", {
      method: "POST",
      body: JSON.stringfy({ comment }),
      headers: {
        "Content-Type": "application/json",
      },
    });
    const data = await response.json();
    console.log(data);
  };

  const deleteComment = async (commentId) => {
    const response = await fetch(`/api/comments/${commentId}`, {
      method: "DELETE",
    });
    const data = await response.json();
    console.log(data);
    fetchComments();
  };

  return (
    <>
      <input type="text" value={comment} onChange={(e) => setComment(e.target.value)} />
      <button onClick={submitComment}>Submit comment</button>
      <button onClick={fetchComments}>Load comments</button>
      {comments.map((comment) => {
        return (
          <div key={comment.id}>
            {comment.id} {comment.text}
            <button onClick={() => deleteComment(comment.id)}>Delete</button>
          </div>
        );
      })}
    </>
  );
}

export default CommentsPage;

(五)Catch All API Routes

1.catch-all routes

1)pages/api/[...params].js

  • 访问 localhost:3000/api 会跳转到 404 页面
export default function handler(req, res) {
  const params = req.query.params; // 根据文件名定义
  console.log(params);
  res.status(200).json(params);
}

2.optional catch-all routes

1)pages/api/[[...params]].js

  • 访问 localhost:3000/api 不会跳转到 404 页面,显示空白,因为当前动态路由页面未定义

(六)APIs and Pre-rendering

1.pages/comments/[commentId].js

import { comments } from "../../data/comments";

function Comment({ comment }) {
  return (
    <div>
      {comment.id}. {comment.text}
    </div>
  );
}

export default Comment;

export async function getStaticPaths() {
  return {
    paths: [{ params: { commentId: "1" } }, { params: { commentId: "2" } }, { params: { commentId: "3" } }],
    fallback: false,
  };
}

export async function getStaticProps(context) {
  const { params } = context;
  const { commentId } = params;

  // 错误:不建议将API路由用于预渲染,直接拿数据操作
  // const response= await fetch(`http://localhost:3000/api/comments/${commentId}`)
  // const data = await response.json()

  const comment = comments.find((comment) => comment.id === parseInt(commentId));
  console.log(comment);

  return {
    props: {
      comment,
    },
  };
}

(七)Summary

  • API 路由机制类似于基于页面的路由机制
  • APIs 将根据文件名被映射成相应的路由
  • 每个 API 路由导出一个默认函数,通常命名为 handler
  • handler 函数接收 requestresponse 两个参数
  • 使用 req.method 满足不同的请求类型,如 GETPOST
  • 不应该为预渲染的内容调用自定义 API 路由
上次编辑于: