第17章 API Routes

Next.js 后端 API 开发入门

作者:zyyc-0336  |  日期:2026-06-12

17.1 前端也能写接口?

传统开发中,前端负责页面,后端负责接口(API)。但 Next.js 让你在同一个项目里写出接口,不用单独搭一个后端服务器。

API Route 是什么?

简单说,它就是一个 URL 地址,当前端访问这个地址时,服务器会执行一段代码并返回数据。

前端页面:  /articles/hello     → 返回 HTML 页面
API Route:/api/comments        → 返回 JSON 数据
💡 通俗理解:页面是给用户看的,API 是给程序用的。API 返回的是纯数据(JSON),不是网页。

17.2 和传统后端的区别

对比项传统后端(Express/Koa)Next.js API Route
需要单独的项目?不用,和前端同项目
需要配服务器?不用,Next.js 自带
独立部署?和前端一起部署
适合场景复杂后端逻辑简单接口、快速原型
💡 如果你的接口逻辑很复杂(比如需要连接多种数据库、处理消息队列),建议用独立后端。如果只是简单的 CRUD 操作,API Route 完全够用。

17.3 文件结构

在 Next.js 的 App Router 中,API Route 就是一个放在 app/ 目录下的 route.js 文件:
src/app/
├── page.js                    # 首页
├── layout.js                  # 布局
└── api/
    └── hello/
        └── route.js           # → 对应 /api/hello
⚠️ 注意:文件名必须是 route.js(或 route.ts),不能是 index.js 或其他名字。

17.4 GET 请求示例

// src/app/api/hello/route.js
import { NextResponse } from 'next/server'

// 处理 GET 请求
export async function GET() {
    return NextResponse.json({
        message: 'Hello, API!',
        time: new Date().toLocaleString()
    })
}

代码解读

代码含义
export async function GET()导出一个叫 GET 的异步函数,处理 GET 请求
NextResponse.json({...})返回一个 JSON 格式的响应
new Date().toLocaleString()获取当前时间
💡 GET 函数名必须全大写,它对应 HTTP 的 GET 方法。Next.js 会根据文件路径自动映射 URL。

17.5 测试接口

启动开发服务器后,打开浏览器访问:

http://localhost:3000/api/hello

你应该看到:

{
  "message": "Hello, API!",
  "time": "2026/6/12 14:30:00"
}
💡 测试 API 也可以用 curl 命令:curl http://localhost:3000/api/hello

17.6 接收 JSON 数据

当前端用 POST 方法提交 JSON 数据时,需要用 await request.json() 来获取:
export async function POST(request) {
    // 注意:json() 是异步的,必须加 await
    const body = await request.json()

    console.log('收到的数据:', body)

    return NextResponse.json({
        success: true,
        received: body
    })
}
⚠️ request.json() 返回的是 Promise,必须加 await,否则你拿到的是一个 Promise 对象,不是实际数据。

17.7 完整示例:评论接口

// src/app/api/comments/route.js
import { NextResponse } from 'next/server'

// 模拟数据库(用数组存储)
const comments = [
    { id: 1, user: '小明', content: '写得不错!', time: '2026-06-10' },
    { id: 2, user: '小红', content: '学到了!', time: '2026-06-11' }
]

// GET:获取所有评论
export async function GET() {
    return NextResponse.json({
        success: true,
        data: comments
    })
}

// POST:添加新评论
export async function POST(request) {
    const body = await request.json()

    // 简单验证
    if (!body.user || !body.content) {
        return NextResponse.json(
            { success: false, message: '用户名和评论内容不能为空' },
            { status: 400 }
        )
    }

    // 创建新评论
    const newComment = {
        id: comments.length + 1,
        user: body.user,
        content: body.content,
        time: new Date().toISOString().split('T')[0]
    }

    comments.push(newComment)

    return NextResponse.json({
        success: true,
        data: newComment,
        message: '评论成功!'
    }, { status: 201 })
}

代码解读

部分含义
const comments = [...]模拟数据库,实际项目会用数据库
if (!body.user || !body.content)验证必填字段
NextResponse.json({...}, { status: 400 })返回错误状态码
{ status: 201 }201 表示"已创建",是 POST 成功的标准状态码

17.8 NextRequest 的常用方法

request 参数是一个 NextRequest 对象,它比原生的 Request 多了很多实用方法:
export async function GET(request) {
    // 获取 URL 查询参数
    const name = request.nextUrl.searchParams.get('name')

    // 获取请求头
    const userAgent = request.headers.get('user-agent')

    // 获取 cookies
    const token = request.cookies.get('token')

    // 获取完整 URL
    const url = request.nextUrl.href

    return NextResponse.json({ name, userAgent, url })
}

常用属性/方法速查

方法作用示例
request.nextUrl.searchParams获取 URL 查询参数/api/hello?name=小明get('name') = '小明'
request.headers获取请求头headers.get('content-type')
request.cookies获取 Cookiecookies.get('token')
request.json()获取 POST 的 JSON 数据await request.json()
request.method获取请求方法'GET''POST'

17.9 NextResponse 的常用方法

// 返回 JSON
return NextResponse.json({ data: 'hello' })

// 返回 JSON 并设置状态码
return NextResponse.json({ error: '未找到' }, { status: 404 })

// 重定向
return NextResponse.redirect(new URL('/login', request.url))

// 设置响应头
const response = NextResponse.json({ data: 'hello' })
response.headers.set('X-Custom-Header', 'my-value')
return response

// 设置 Cookie
const response = NextResponse.json({ success: true })
response.cookies.set('token', 'abc123', { httpOnly: true })
return response

17.10 获取 URL 参数

查询参数(?key=value

// 访问:/api/articles?page=2&limit=10
export async function GET(request) {
    const page = request.nextUrl.searchParams.get('page')   // '2'
    const limit = request.nextUrl.searchParams.get('limit')  // '10'

    return NextResponse.json({ page, limit })
}

动态路由参数(放在文件夹名里)

src/app/api/articles/[id]/route.js  →  /api/articles/123
// src/app/api/articles/[id]/route.js
export async function GET(request, { params }) {
    const { id } = await params  // 注意:params 是异步的,需要 await
    return NextResponse.json({ articleId: id })
}
⚠️ 在 Next.js 15+ 中,params 是一个 Promise,必须用 await 获取。

17.11 接口设计

接口方法说明请求体
/api/commentsGET获取文章的所有评论
/api/commentsPOST提交新评论{ user, content, articleId }

17.12 完整代码

📄 查看完整 comments/route.js 代码
// src/app/api/comments/route.js
import { NextResponse } from 'next/server'

// 模拟数据库
let comments = [
    { id: 1, articleId: 'hello-world', user: '小明', content: '写得不错!', time: '2026-06-10' },
    { id: 2, articleId: 'hello-world', user: '小红', content: '学到了!', time: '2026-06-11' },
    { id: 3, articleId: 'react-basics', user: '小刚', content: '感谢分享', time: '2026-06-11' }
]

// GET:获取评论(支持按文章筛选)
export async function GET(request) {
    const articleId = request.nextUrl.searchParams.get('articleId')

    let result = comments
    if (articleId) {
        result = comments.filter(c => c.articleId === articleId)
    }

    return NextResponse.json({
        success: true,
        total: result.length,
        data: result
    })
}

// POST:添加评论
export async function POST(request) {
    try {
        const body = await request.json()

        // 验证必填字段
        if (!body.user || !body.content || !body.articleId) {
            return NextResponse.json(
                { success: false, message: '缺少必填字段:user、content、articleId' },
                { status: 400 }
            )
        }

        // 验证内容长度
        if (body.content.length > 500) {
            return NextResponse.json(
                { success: false, message: '评论内容不能超过 500 字' },
                { status: 400 }
            )
        }

        // 创建新评论
        const newComment = {
            id: comments.length + 1,
            articleId: body.articleId,
            user: body.user,
            content: body.content,
            time: new Date().toISOString().split('T')[0]
        }

        comments.push(newComment)

        return NextResponse.json({
            success: true,
            data: newComment,
            message: '评论成功!'
        }, { status: 201 })

    } catch (error) {
        return NextResponse.json(
            { success: false, message: '请求格式错误,请发送 JSON' },
            { status: 400 }
        )
    }
}

17.13 前端调用

在文章页面中调用 POST 接口提交评论:

📄 查看前端调用代码 (page.js)
// src/app/articles/[slug]/page.js(部分代码)
'use client'

import { useState } from 'react'

export default function ArticlePage() {
    const [comments, setComments] = useState([])
    const [user, setUser] = useState('')
    const [content, setContent] = useState('')

    // 提交评论
    async function handleSubmit(e) {
        e.preventDefault()

        // 发送 POST 请求
        const res = await fetch('/api/comments', {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify({
                user: user,
                content: content,
                articleId: 'hello-world'
            })
        })

        const data = await res.json()

        if (data.success) {
            setComments([...comments, data.data])
            setContent('')
            alert('评论成功!')
        } else {
            alert('评论失败:' + data.message)
        }
    }

    return (
        <div>
            <h1>文章页面</h1>

            {/* 评论表单 */}
            <form onSubmit={handleSubmit}>
                <input
                    value={user}
                    onChange={(e) => setUser(e.target.value)}
                    placeholder="你的名字"
                    required
                />
                <textarea
                    value={content}
                    onChange={(e) => setContent(e.target.value)}
                    placeholder="写下你的评论..."
                    required
                />
                <button type="submit">提交评论</button>
            </form>

            {/* 评论列表 */}
            <div>
                {comments.map(c => (
                    <div key={c.id}>
                        <strong>{c.user}</strong> · {c.time}
                        <p>{c.content}</p>
                    </div>
                ))}
            </div>
        </div>
    )
}

调用流程

用户点击"提交评论" ↓ 前端 fetch('/api/comments', { method: 'POST', body: ... }) ↓ Next.js 找到 app/api/comments/route.js 的 POST 函数 ↓ 执行 POST 函数,解析请求体,创建评论 ↓ 返回 JSON { success: true, data: {...} } ↓ 前端收到响应,更新页面显示
💡 这就是前后端一体开发的魅力——不需要跨域、不需要单独部署后端,前端直接调用自己项目的接口。

本章复盘——API Routes 核心要点

要点说明
API Routespages/api 目录下创建后端接口
请求处理req.method 区分 GET/POST,req.body 获取数据
动态路由[slug].js 处理动态参数
数据验证服务端校验输入,防止恶意请求

下一章预告(第18章):我们将学习样式与资源处理——CSS Modules、图片优化和字体加载。

本文档由 zyyc-0336 编写,最后更新:2026-06-14