# BFF **Repository Path**: five-tiger-generals/bff ## Basic Information - **Project Name**: BFF - **Description**: No description available - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: main - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-02-10 - **Last Updated**: 2026-02-11 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # BFF 聚合层 (my-bffTs) 基于 **Express 5 + TypeScript** 的 BFF(Backend For Frontend)聚合层脚手架。 ## 快速开始 ```bash # 1. 安装依赖 npm install # 2. 复制环境变量 cp .env.example .env # 3. 启动开发服务 npm run dev # 4. 访问健康检查 curl http://localhost:4000/health ``` ## 目录结构 ``` src/ ├── index.ts # 入口文件(app 组装 + 启动 + 优雅停机) ├── config/ │ └── index.ts # 环境变量集中管理 + Fail-Fast 校验 ├── types/ │ ├── index.ts # 类型统一导出 │ ├── common.ts # 通用类型(RawApiResponse 等) │ ├── statistics.ts # 统计相关类型 │ ├── user.ts # 用户相关类型 │ └── order.ts # 订单相关类型 ├── utils/ │ ├── httpClient.ts # axios 统一实例(超时、拦截器、自动透传 satoken) │ ├── requestContext.ts # AsyncLocalStorage 请求上下文(跨调用链传递 satoken) │ └── response.ts # 统一响应工具函数(success / fail) ├── middlewares/ │ ├── authGuard.ts # 鉴权守卫(白名单 + satoken 校验 + 上下文注入) │ ├── errorHandler.ts # 全局错误处理 │ └── requestLogger.ts # 请求日志(morgan) ├── routes/ │ ├── index.ts # 路由汇总入口 │ ├── home.ts # 首页统计路由 │ ├── dashboard.ts # 仪表盘路由 │ └── order.ts # 订单路由 ├── controllers/ │ ├── statisticsController.ts # 首页统计数据聚合 │ ├── dashboardController.ts # 仪表盘聚合(用户 + 订单) │ └── orderController.ts # 订单操作 └── services/ ├── statisticsService.ts # 下游统计服务调用(设备、用户、活跃数) ├── userService.ts # 下游用户服务调用 └── orderService.ts # 下游订单服务调用 ``` ## 分层职责 | 层级 | 文件位置 | 职责 | |------|----------|------| | **Route** | `src/routes/` | 只做 URL → Controller 的映射 | | **Controller** | `src/controllers/` | 业务编排:调用多个 service、聚合数据、裁剪字段 | | **Service** | `src/services/` | 调用下游微服务,做数据转换 | | **Types** | `src/types/` | Raw(下游原始结构)+ VO(前端需要的结构) | | **Utils** | `src/utils/` | httpClient、请求上下文、统一响应 | | **Middlewares** | `src/middlewares/` | 鉴权守卫、错误处理、请求日志 | | **Config** | `src/config/` | 环境变量集中管理 | ## 鉴权机制 BFF 层通过 `authGuard` 中间件统一处理 satoken 鉴权: ### 白名单 以下接口无需携带 satoken,用于登录前的场景: ```ts const WHITE_LIST = [ '/login', '/register', '/captcha', ]; ``` ### 请求流程 ``` 前端请求 → /api/xxx ↓ authGuard 中间件: ├── 命中白名单(如 /api/login)→ 放行 └── 非白名单: ├── 有 satoken → 存入请求上下文,放行 └── 无 satoken → 返回 401,不请求后端 ↓ Controller → Service → httpClient(自动带 satoken)→ 下游服务 ``` ### satoken 透传原理 1. `authGuard` 从 `req.headers.satoken` 提取 token,存入 `AsyncLocalStorage` 2. `httpClient` 请求拦截器从 `AsyncLocalStorage` 读取 token,自动注入下游请求头 3. 各 Controller / Service 无需手动传递 token ## 如何新增一个 BFF 接口 以新增"商品详情"接口为例: ### 1. 定义类型 `src/types/product.ts` ```ts // 下游服务原始数据 export interface RawProduct { id: string; title: string; price: number; // ...下游返回的所有字段 } // 给前端的裁剪数据 export interface ProductVO { id: string; name: string; price: number; } ``` 在 `src/types/index.ts` 中导出: ```ts export * from './product'; ``` ### 2. 创建 Service `src/services/productService.ts` ```ts import httpClient from '../utils/httpClient'; import { config } from '../config'; import { RawProduct, ProductVO } from '../types'; export async function getProductById(productId: string): Promise { const { data } = await httpClient.get( `${config.productServiceUrl}/products/${productId}`, ); return { id: data.id, name: data.title, price: data.price, }; } ``` ### 3. 创建 Controller `src/controllers/productController.ts` ```ts import { Request, Response } from 'express'; import { getProductById } from '../services/productService'; import { success, fail } from '../utils/response'; export async function getProduct(req: Request, res: Response) { const { id } = req.params; if (!id) return fail(res, '缺少参数 id'); try { const product = await getProductById(id); return success(res, product); } catch (err: any) { return fail(res, '获取商品失败', 502); } } ``` ### 4. 创建路由 `src/routes/product.ts` ```ts import { Router } from 'express'; import { getProduct } from '../controllers/productController'; const router = Router(); router.get('/product/:id', getProduct); export default router; ``` ### 5. 在 `src/routes/index.ts` 中注册路由 ```ts import productRouter from './product'; router.use(productRouter); ``` > 如果新接口属于白名单(无需鉴权),需要在 `src/middlewares/authGuard.ts` 的 `WHITE_LIST` 中添加对应路径。 ## 可用脚本 | 命令 | 说明 | |------|------| | `npm run dev` | 启动开发服务(tsx watch 热重载) | | `npm run build` | 编译 TypeScript → dist/ | | `npm start` | 运行编译后的生产代码 | ## 环境变量 参见 [.env.example](./.env.example),所有必需变量缺失时启动会直接报错(Fail-Fast)。 | 变量 | 必需 | 说明 | 默认值 | |------|------|------|--------| | `PORT` | 否 | 服务端口 | `4000` | | `NODE_ENV` | 否 | 运行环境 | `development` | | `CORS_ORIGIN` | 否 | 允许跨域的前端域名 | `*` | | `USER_SERVICE_URL` | 是 | 下游用户服务地址 | - | | `ORDER_SERVICE_URL` | 是 | 下游订单服务地址 | - | | `PRODUCT_SERVICE_URL` | 是 | 下游商品服务地址 | - | | `APP_BASE_API` | 是 | 下游统计服务基础地址 | - | ## 技术栈 - **Express 5** — Web 框架 - **TypeScript** — 类型安全 - **Axios** — HTTP 客户端(带统一拦截器、satoken 自动透传) - **Helmet** — 安全响应头 - **CORS** — 跨域处理 - **Morgan** — 请求日志 - **tsx** — 开发环境热重载(替代 ts-node + nodemon)