Next.js的服務(wù)端路由之對應(yīng)api文件夾詳解
一、 api 文件夾的作用
在 Next.js中,api 文件夾 來定義 API 路由,它的作用是:
- 在
Next.js項(xiàng)目里快速創(chuàng)建一個(gè)后端接口(Node.js 環(huán)境下運(yùn)行,不會(huì)被打包到前端)。 - 用來處理數(shù)據(jù)請求,比如登錄接口、數(shù)據(jù)庫查詢、調(diào)用第三方 API 等。
- 避免額外起一個(gè) Express/Koa/Fastify 服務(wù),前后端一體化。
app/api (Next.js 15 版本)的基本規(guī)則
- 所有在 pages/api 下的文件都會(huì)自動(dòng)變成一個(gè) API 路由。
- 文件名 = 接口路徑。
- 導(dǎo)出的函數(shù)接收 (req, res) 兩個(gè)參數(shù)(跟 Express 很像)。
例子:
// app/api/hello/route.js
export async function GET() {
return new Response(JSON.stringify({ message: "Hello from API!" }), {
status: 200,
headers: { "Content-Type": "application/json" }
});
}
訪問 http://localhost:3000/api/hello 會(huì)返回:
{
"message": "Hello from API!"
}支持 REST API
- 支持
GET、POST、PUT、DELETE等 HTTP 方法。 - 可以根據(jù) HTTP 方法來導(dǎo)出不同的函數(shù)。
- 例如:GET 方法導(dǎo)出
GET函數(shù),POST 方法導(dǎo)出POST函數(shù)。
// app/api/hello/route.js
export async function GET() {
return new Response(JSON.stringify({ message: "Hello from API!" }), {
status: 200,
headers: { "Content-Type": "application/json" }
});
}
export async function POST(req) {
const { name } = await req.json();
return new Response(JSON.stringify({ message: `Hello, ${name}!` }), {
status: 200,
headers: { "Content-Type": "application/json" }
});
}
可做后端邏輯
- 讀寫數(shù)據(jù)庫(MySQL、MongoDB、Prisma 等)
- 調(diào)用外部 API(比如 OpenAI API)
- 處理鑒權(quán) / Session / Token
? 總結(jié): /app/api/ 就是 Next.js 內(nèi)置的“后端”,可以讓前端項(xiàng)目直接帶上后端接口。特別適合中小項(xiàng)目,不用額外搭建 Express/FastAPI,就能快速完成前后端一體化開發(fā)。
二、基本使用
案例使用的是 Next.js 15,默認(rèn)走的是 App Router
新建文件:
app / api / hello / route.js;
內(nèi)容:
// app/api/hello/route.js
export async function GET() {
return new Response(JSON.stringify({ message: "Hello from API!" }), {
status: 200,
headers: { "Content-Type": "application/json" }
});
}
瀏覽器訪問:http://localhost:3000/api/hello 返回:
{ "message": "Hello from API!" }流程已跑通!
三、連接 MySQL 的 API 路由 案例
下面是是一個(gè)基于 Next.js 15 + MySQL 的 API 路由示例(基于 App Router 結(jié)構(gòu))。
使用 mysql2,并放在 lib/db.js 里維護(hù)數(shù)據(jù)庫連接池。
安裝依賴
pnpm install mysql2
新建數(shù)據(jù)庫連接(lib/db.js)
// lib/db.js
import mysql from "mysql2/promise";
const pool = mysql.createPool({
host: "localhost", // 數(shù)據(jù)庫地址
user: "root", // 數(shù)據(jù)庫用戶
password: "xinjie", // 數(shù)據(jù)庫密碼
database: "ai-chat-db", // 數(shù)據(jù)庫名
waitForConnections: true,
connectionLimit: 10,
queueLimit: 0
});
export default pool;
API 路由(app/api/users/route.js)
// app/api/users/route.js
import pool from "@/lib/db";
// GET /api/users → 查詢所有用戶
export async function GET() {
try {
const [rows] = await pool.query("SELECT * FROM users");
return new Response(JSON.stringify(rows), {
status: 200,
headers: { "Content-Type": "application/json" }
});
} catch (error) {
return new Response(JSON.stringify({ error: error.message }), {
status: 500,
headers: { "Content-Type": "application/json" }
});
}
}
// POST /api/users → 新增用戶
export async function POST(req) {
try {
const { username, email, password } = await req.json();
if (!username || !email || !password) {
return new Response(JSON.stringify({ error: "缺少 username、email 或 password 參數(shù)" }), {
status: 400,
headers: { "Content-Type": "application/json" }
});
}
// 對密碼進(jìn)行哈希處理
const bcrypt = require("bcrypt");
const saltRounds = 12;
const password_hash = await bcrypt.hash(password, saltRounds);
// 設(shè)置默認(rèn)值
const is_active = 1;
const is_admin = 0; // 默認(rèn)普通用戶
const currentTime = new Date().toISOString().slice(0, 19).replace("T", " ");
const [result] = await pool.query(
"INSERT INTO users (username, email, password_hash, is_active, is_admin, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?)",
[username, email, password_hash, is_active, is_admin, currentTime, currentTime]
);
return new Response(
JSON.stringify({
id: result.insertId,
username,
email,
is_active,
is_admin,
created_at: currentTime,
updated_at: currentTime
}),
{
status: 201,
headers: { "Content-Type": "application/json" }
}
);
} catch (error) {
return new Response(JSON.stringify({ error: error.message }), {
status: 500,
headers: { "Content-Type": "application/json" }
});
}
}
數(shù)據(jù)庫建表
在 MySQL 里執(zhí)行:
CREATE TABLE users (
id INT PRIMARY KEY AUTO_INCREMENT,
username VARCHAR(50) NOT NULL UNIQUE,
email VARCHAR(100) NOT NULL UNIQUE,
password_hash VARCHAR(255) NOT NULL,
is_active TINYINT(1) DEFAULT 1,
is_admin TINYINT(1) DEFAULT 0,
created_at DATETIME,
updated_at DATETIME
);
調(diào)用接口
- 查詢用戶 (GET)
GET http://localhost:3000/api/users
返回如下:

可以看到數(shù)據(jù)庫中的三條數(shù)據(jù)全部查出來了。
- 新增用戶 (POST)
POST http://localhost:3000/api/users
Content-Type: application/json
{
"username": "newuser",
"email": "newuser@example.com",
"password": "user123"
}
也可以在apiFox中調(diào)用:

調(diào)用成功,數(shù)據(jù)庫截圖如下:

四、前端頁面調(diào)用 api 接口 案例
下面是新增一個(gè)前端頁面(app/users/page.js),在瀏覽器里調(diào)用 /api/users 并顯示用戶列表。模擬日常工作前端頁面調(diào)用 api 接口的情況。
新建頁面文件
在 app/users/page.js:
"use client";
import { useState, useEffect } from "react";
export default function UsersPage() {
const [users, setUsers] = useState([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
// 修改表單狀態(tài)初始化,添加password字段
const [form, setForm] = useState({ username: "", email: "", password: "" });
// 獲取用戶列表
useEffect(() => {
async function fetchUsers() {
try {
const res = await fetch("/api/users");
if (!res.ok) throw new Error("獲取用戶失敗");
const data = await res.json();
setUsers(data);
} catch (err) {
setError(err.message);
} finally {
setLoading(false);
}
}
fetchUsers();
}, []);
// 提交表單 -> 新增用戶
async function handleSubmit(e) {
e.preventDefault();
try {
const res = await fetch("/api/users", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(form)
});
if (!res.ok) throw new Error("新增失敗");
const newUser = await res.json();
setUsers([...users, newUser]);
setForm({ username: "", email: "" });
} catch (err) {
alert(err.message);
}
}
if (loading) return <p>加載中...</p>;
if (error) return <p style={{ color: "red" }}>錯(cuò)誤: {error}</p>;
return (
<div style={{ padding: "20px" }}>
<h1>用戶管理</h1>
{/* 新增用戶表單 */}
// 在表單中添加密碼輸入框
<form onSubmit={handleSubmit} style={{ marginBottom: "20px" }}>
<input type="text" placeholder="姓名" value={form.username} onChange={(e) => setForm({ ...form, username: e.target.value })} required />
<input type="email" placeholder="郵箱" value={form.email} onChange={(e) => setForm({ ...form, email: e.target.value })} required />
<input type="password" placeholder="密碼" value={form.password} onChange={(e) => setForm({ ...form, password: e.target.value })} required />
<button type="submit">添加用戶</button>
</form>
{/* 用戶列表 */}
<ul>
{users.length === 0 ? (
<p>暫無用戶</p>
) : (
users.map((user) => (
<li key={user.id}>
{user.username} - {user.email}
</li>
))
)}
</ul>
</div>
);
}
頁面效果
- 進(jìn)入
http://localhost:3000/users - 頁面會(huì)自動(dòng)請求
/api/users,顯示用戶列表 - 可以在輸入框填寫 姓名 / 郵箱 并點(diǎn)擊“添加用戶” → 會(huì)直接寫入 MySQL,再刷新用戶列表

點(diǎn)擊添加用戶后,數(shù)據(jù)庫會(huì)新增一條數(shù)據(jù),如下所示。

結(jié)構(gòu)回顧
完整目錄結(jié)構(gòu)大概是:
app/ ├── api/ │ └── users/ │ └── route.js # API 路由 ├── users/ │ └── page.js # 前端頁面 lib/ └── db.js # 數(shù)據(jù)庫連接
這樣,就完成了一個(gè) 全棧小應(yīng)用: ? Next.js 15 (App Router) ? MySQL 存儲(chǔ) ? API 路由 ? 前端頁面
總結(jié)
到此這篇關(guān)于Next.js的服務(wù)端路由之對應(yīng)api文件夾詳解的文章就介紹到這了,更多相關(guān)Next.js對應(yīng)api文件夾內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
詳解使用Next.js構(gòu)建服務(wù)端渲染應(yīng)用
這篇文章主要介紹了詳解使用Next.js構(gòu)建服務(wù)端渲染應(yīng)用,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-07-07
如何在webpack項(xiàng)目中調(diào)試loader插件
最近在學(xué)習(xí)webpack,本文主要介紹了loader插件的調(diào)試方法,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-06-06
js jquery ajax的幾種用法總結(jié)(及優(yōu)缺點(diǎn)介紹)
本篇文章只要介紹了js jquery ajax的幾種用法總結(jié)(及優(yōu)缺點(diǎn)介紹),需要的朋友可以過來參考下,希望對大家有所幫助2014-01-01

