news 2026/9/18 23:26:20

Next.js + FastAPI 服务绑定(Service Bindings)实战:用 vercel.json 构建内部后端服务

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Next.js + FastAPI 服务绑定(Service Bindings)实战:用 vercel.json 构建内部后端服务

Next.js + FastAPI 服务绑定(Service Bindings)实战:用 vercel.json 构建内部后端服务

【免费下载链接】examplesEnjoy our curated collection of examples and solutions. Use these patterns to build your own robust and scalable applications.项目地址: https://gitcode.com/GitHub_Trending/examples1/examples

导读

本文基于开源仓库services/nextjs-fastapi-bindings示例,讲解如何在 Vercel Services 架构中同时部署 Next.js 前端与 FastAPI 后端,并通过service bindings(服务绑定)将后端地址以环境变量BACKEND_URL的形式注入前端,实现"后端不对外暴露、仅由前端 API Route 内部代理调用"的经典前后端分离模式。读完本文,你将掌握vercel.jsonservicesbindingsrewrites三者的配置关系,以及 Next.js Route Handler 转发请求到 FastAPI 的完整链路。

一、示例概览:两个服务、一条私有链路

该示例是一个极简的 Vercel Services 演示,包含两个服务:

  • frontend(Next.js):挂载在/,对外提供页面与 API Route;
  • backend(FastAPI):仅内部可达,不直接对外暴露任何路径。

它演示了三件事:

  1. 一个公开的Next.js API 路由/api/hello
  2. 一个不对外公开的 FastAPI 后端路由/status/items等);
  3. 通过vercel.json中的bindings实现服务间的内部调用。

核心链路是:浏览器 → Next.js API Route(/api/backend/...)→ 环境变量BACKEND_URL→ 内部 FastAPI 服务。这样后端无需公网入口,减少了攻击面,也把"谁可以访问后端"的决策权收敛到前端代码层。

二、项目结构

仓库中该示例的完整目录结构如下(见 services/nextjs-fastapi-bindings):

nextjs-fastapi-bindings/ ├── backend/ │ ├── main.py │ ├── pyproject.toml │ └── uv.lock ├── frontend/ │ ├── app/ │ │ ├── api/backend/[[...path]]/route.js │ │ ├── api/hello/route.js │ │ ├── globals.css │ │ ├── layout.js │ │ └── page.js │ ├── next.config.js │ ├── package.json │ └── pnpm-lock.yaml └── vercel.json

两个明显的工程要点:

  • frontend/app/api/backend/[[...path]]/route.js使用 Next.js 的catch-all 动态路由段,把/api/backend/*下的任意子路径统一转发给 FastAPI;
  • backend/除了main.pypyproject.toml,还带有uv.lock,说明后端依赖由 uv 管理(仓库内以uv.lock锁定依赖版本)。

三、Services 配置详解:vercel.json 是编排核心

整个架构的"总开关"在仓库根目录的 vercel.json,完整内容如下:

{ "$schema": "https://openapi.vercel.sh/vercel.json", "services": { "frontend": { "root": "frontend/", "framework": "nextjs", "bindings": [ { "type": "service", "service": "backend", "format": "url", "env": "BACKEND_URL" } ] }, "backend": { "root": "backend/", "entrypoint": "main:app" } }, "rewrites": [ { "source": "/(.*)", "destination": { "service": "frontend" } } ] }

3.1 services:声明两个服务

services字段声明了仓库内的两个子服务,每个服务用root指定代码所在目录:

字段frontend 取值backend 取值说明
rootfrontend/backend/服务源码所在目录
frameworknextjs未声明前端显式指定框架;后端为 Python ASGI 应用,无需框架字段
entrypoint未声明main:app指定 FastAPI 实例,mainbackend/main.py模块,app是该文件中的FastAPI实例对象
bindings配置了一条绑定见 3.2 节

后端服务在 backend/main.py 中创建:

app = FastAPI( title="Next.js + FastAPI Services Demo", description="Internal backend service — accessed via Next.js API routes using a service binding", version="1.0.0" )

entrypoint: "main:app"中的app正是这里创建的 FastAPI 实例。注意pyproject.toml(见 backend/pyproject.toml)要求 Python>=3.12,并依赖fastapi>=0.124.2uvicorn>=0.38.0

3.2 bindings:把后端地址注入为环境变量

bindings是本示例区别于"公共路由代理"方案(可对比同仓库的 services/nextjs-fastapi/vercel.json)的关键所在:

"bindings": [ { "type": "service", "service": "backend", "format": "url", "env": "BACKEND_URL" } ]

各字段含义:

  • type: "service":绑定类型为服务绑定,即把另一个服务作为依赖注入;
  • service: "backend":被绑定的目标服务名,必须与services中声明的键一致;
  • format: "url":注入形式为 URL 字符串;
  • env: "BACKEND_URL":注入到 frontend 服务中的环境变量名。

部署后,frontend 服务内即可通过process.env.BACKEND_URL拿到后端服务的内部地址。由于该地址是 Vercel 平台动态分配的,不应该在代码里硬编码,这正是绑定机制的价值。

3.3 rewrites:把所有流量先交给 frontend

"rewrites": [ { "source": "/(.*)", "destination": { "service": "frontend" } } ]

该规则将根路径/(.*)的所有请求重写到 frontend 服务。也就是说,对外的入口只有 frontend,后端服务没有出现在任何 rewrite 的destination中,因此从公网无法直接访问 FastAPI。

四、后端实现:内部 FastAPI 服务

backend/main.py 是一个标准且极简的 FastAPI 应用,除根路径外提供了三个业务路由:

@app.get("/") def read_root(): return {"message": "FastAPI service is running"} @app.get("/status") def get_status(): return { "service": "backend", "framework": "fastapi", "timestamp": datetime.now(timezone.utc).isoformat(), } @app.get("/items") def get_items(): return {"items": SAMPLE_ITEMS, "count": len(SAMPLE_ITEMS)} @app.get("/items/{item_id}") def get_item(item_id: int): item = next((value for value in SAMPLE_ITEMS if value["id"] == item_id), None) if item is None: raise HTTPException(status_code=404, detail="Item not found") return {"item": item} @app.get("/{blah:path}") def fallback(blah): print(blah) return blah

代码要点:

  • 内置了三条示例数据(SAMPLE_ITEMS),用于演示列表查询与按 ID 查询;
  • /items/{item_id}使用类型注解item_id: int,由 FastAPI 自动做路径参数校验;查不到时抛出HTTPException(404)
  • 末尾的/{blah:path}catch-all 路由会把未匹配的路径原样返回,可用于调试转发链路是否到达后端。

五、前端实现:API Route 充当反向代理

5.1 直接响应的 Next.js API 路由

frontend/app/api/hello/route.js 是一个完全运行在 Next.js 服务内的 Route Handler:

import { NextResponse } from "next/server"; export async function GET() { return NextResponse.json({ service: "frontend", framework: "nextjs", message: "Hello from Next.js API route", timestamp: new Date().toISOString(), }); }

它不依赖任何后端,直接返回 JSON,用于验证 frontend 服务本身工作正常。

5.2 转发到 FastAPI 的代理路由

frontend/app/api/backend/[[...path]]/route.js 是整个示例的核心代理逻辑:

export async function GET(request, { params }) { const { path } = await params; const url = new URL(path ? path.join("/") : "", process.env.BACKEND_URL); url.search = request.nextUrl.search const res = await fetch(url, { cache: "no-store" }); const data = await res.json(); return Response.json(data, { status: res.status }); }

逐行解读:

  1. catch-all 参数[[...path]]是 Next.js 的可选 catch-all 动态段。访问/api/backend/statuspath = ["status"];访问/api/backend/items/1path = ["items", "1"]
  2. 拼接内部地址path.join("/")还原出/status/items/1等子路径,再以process.env.BACKEND_URL为基址构造完整 URL。这一步正是消费了vercel.json中 binding 注入的环境变量;
  3. 透传查询参数url.search = request.nextUrl.search把浏览器请求的 query string 原样带到后端;
  4. 实时转发fetch(url, { cache: "no-store" })禁用缓存,确保每次请求都实时打到 FastAPI(前端页面 page.js 中的调用也使用cache: "no-store",行为一致);
  5. 透传状态码Response.json(data, { status: res.status })不仅回传 JSON 体,还把后端的 HTTP 状态码(如 404)一并透传给浏览器。

由此,/api/backend/*在语义上成为 FastAPI 的"命名空间化"镜像,前端代码无需关心后端真实地址。

六、前端页面与本地运行

6.1 交互页面

frontend/app/page.js 是一个客户端组件,提供三个交互入口:

  • Call/api/hello:请求 Next.js 自身的 API 路由;
  • Call/api/backend/status:经代理路由访问 FastAPI 的/status
  • Open/api/backend/items:直接打开后端示例数据。

页面导航栏还提供了StatusItems两个链接(对应/api/backend/status/api/backend/items)。返回的 JSON 会统一渲染在页面下方的响应区块中,便于直观对比"来自 frontend 的响应"与"来自 backend 的响应"字段差异。

6.2 本地运行

示例依赖 Vercel CLI,在项目根目录(即vercel.json所在目录)执行:

vercel dev

启动后打开http://localhost:3000,依次访问:

  • /api/hello—— Next.js API 路由,返回service: "frontend"
  • /api/backend/status—— 经 Next.js 代理路由转发到 FastAPI,返回service: "backend"及 UTC 时间戳;
  • /api/backend/items—— 返回后端内置的三条示例数据;
  • /api/backend/items/1—— 返回单条示例数据;访问不存在的 ID(如/api/backend/items/999)会得到 404。

本地开发时vercel dev会模拟vercel.json中的服务编排与绑定注入,因此process.env.BACKEND_URL在本地同样可用,无需手工配置环境变量。

七、与其他方案的对比与适用场景

同仓库还提供了姊妹示例 services/nextjs-fastapi:它通过 rewrite 把/svc/api公开路由到 FastAPI,后端可从公网直接访问。两者对比可以更清晰地理解 bindings 的定位:

维度nextjs-fastapi(rewrites 方案)nextjs-fastapi-bindings(bindings 方案)
后端可达性/svc/api对外公开仅内部可达,无公网入口
后端地址获取由路由规则固定通过BACKEND_URL环境变量注入
适合场景需要独立域名/路径访问后端的场景后端仅服务前端、希望收敛攻击面的场景

如果你的 FastAPI 只是给同一个 Next.js 应用提供数据接口、不需要被第三方直接调用,bindings 方案是更安全的默认选择;后端地址由平台注入,也避免了在代码或构建配置中硬编码。

八、小结

通过services/nextjs-fastapi-bindings这个最小示例,你可以完整掌握 Vercel Services 的三种核心编排能力:

  1. services:声明 frontend 与 backend 两个子服务及其入口(entrypoint: "main:app");
  2. bindings:把后端以 URL 形式绑定为前端的BACKEND_URL环境变量,实现内部调用;
  3. rewrites:把对外流量全部导向 frontend,确保 FastAPI 不暴露在公网。

再加上 Next.js 的 catch-all Route Handler 作为转发层,即可在几行代码内搭建一套"前端对外、后端内聚"的多语言微服务架构。如需对照公开路由方案的差异,可继续阅读同仓库的 services/nextjs-fastapi 示例。

【免费下载链接】examplesEnjoy our curated collection of examples and solutions. Use these patterns to build your own robust and scalable applications.项目地址: https://gitcode.com/GitHub_Trending/examples1/examples

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/18 23:16:59

用Python与正则解析.doc复习题,打造命令行自测工具

简介:人教版高一英语必修二总复习单项选择题是一份面向高一学生与英语教师的复习资料,针对必修二常考语法点和词汇搭配设计,可帮助练习者在考前快速梳理易错考点,也能为教师选题组卷或课堂小测提供现成素材。题目围绕高频短语、定…

作者头像 李华