四、API Routes
大约 3 分钟约 911 字
(一)API 路由
NextJS
是一个完整的堆栈框架- 可以在
React
中编写前端代码,也可以编写被前端代码调用的API
- API 路由允许创建
RESTful
端点作为NextJS
项目的文件夹结构的一部分 - 在
pages/api/*
文件夹下,可以为项目定义各种API
- 不需要编写任何额外的服务器代码就可以添加业务逻辑,也不需要配置任何
API
路由
- 不需要编写任何额外的服务器代码就可以添加业务逻辑,也不需要配置任何
NextJS
提供了编写全栈React+Node
应用程序所需的一切
pages/api/index.js
1.- 访问
localhost:3000/api
export default function handler(req, res) {
res.status(200).json({
name: "Home API route",
});
}
pages/api/dashboard.js
2.- 访问
localhost:3000/api/dashboard
export default function handler(req, res) {
res.status(200).json({
name: "Dashboard API route",
});
}
pages/api/blog/index.js
3.- 访问
localhost:3000/api/blog
export default function handler(req, res) {
res.status(200).json({
name: "Blog API route",
});
}
(二)API GET Request
data/comments.js
1.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",
},
];
pages/api/comments/index.js
2.import { comments } from "../../../data/comments";
export default function handler(req, res) {
res.status(200).json(comments);
}
pages/comments/index.js
3.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
pages/api/comments/index.js
1.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);
}
}
pages/comments/index.js
2.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
pages/api/comments/[commentId].js
1.- 访问
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);
}
}
pages/comments/index.js
2.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
pages/api/[...params].js
1)- 访问
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
pages/api/[[...params]].js
1)- 访问
localhost:3000/api
不会跳转到 404 页面,显示空白,因为当前动态路由页面未定义
(六)APIs and Pre-rendering
pages/comments/[commentId].js
1.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
函数接收request
和response
两个参数- 使用
req.method
满足不同的请求类型,如GET
和POST
- 不应该为预渲染的内容调用自定义 API 路由