在 Monorepo 中统一管理 Next.js 后端 API 与 Flutter 移动端项目:实现 API 接口定义的同步
之前在两个项目中分别管理接口实现和使用,在手工同步 API 定义的过程中吃了不少亏。后发现 Monorepo,于是在新项目进行尝试。
手动维护两套 API 客户端代码不仅繁琐,而且极易出错。本文将介绍一种优雅的解决方案:通过 Monorepo 架构和自动化工具,实现 API 接口定义的“一次编写,处处使用”。
为什么选择 Monorepo?
Monorepo(单体仓库)是一种将多个相关项目的代码存储在同一个代码仓库中的策略。对于我们的场景,即一个 Next.js 后端 API 和一个或多个 Flutter 应用,Monorepo 带来了显著的优势:
- 依赖共享:公共的配置文件、工具库,甚至 API 类型定义,都可以被所有项目轻松引用。
- 原子提交:对 API 的修改和对客户端的适配可以在同一个提交中完成,保证了代码库的一致性。
核心思路:API 契约优先
我们的核心策略是采用“契约优先”的开发模式。这意味着我们将 API 的接口定义(使用 OpenAPI/Swagger 规范)视为前后端之间的“契约”。
- 后端(Next.js):负责编写业务逻辑,并自动生成一份最新的
openapi.json文件。 - 共享层(packages/api-spec):存放这份
openapi.json“契约”文件。 - 客户端(Flutter):监听“契约”的变化,并自动生成对应的 Dart 模型和网络请求代码。
通过这种方式,Flutter 客户端的代码始终与 Next.js 后端的 API 保持同步,从根本上杜绝了因接口不一致导致的运行时错误。
项目结构
一个典型的 Monorepo 项目结构如下所示:
my-fullstack-app/
├── apps/
│ ├── nextjs-api/ # Next.js 后端 API 项目
│ │ ├── pages/
│ │ │ └── api/
│ │ │ └── users.ts # API 路由
│ │ ├── scripts/
│ │ │ └── generate-spec.ts # 生成 OpenAPI 规范的脚本
│ │ └── package.json
│ └── flutter-app/ # Flutter 移动端应用
│ ├── lib/
│ │ ├── generated/ # 自动生成的 Dart 代码目录
│ │ └── main.dart
│ └── pubspec.yaml
├── packages/
│ └── api-spec/ # 共享的 API 规范包
│ └── openapi.json # 核心的“契约”文件
├── package.json # 根目录 package.json (用于工作区管理)
└── pnpm-workspace.yaml # pnpm 工作区配置
第一步:配置 Next.js 后端生成 API 规范
Next.js 本身不提供 OpenAPI 规范生成功能,但我们可以借助 next-swagger-doc 等库来实现。
-
安装依赖:在
apps/nextjs-api目录下安装必要的包。cd apps/nextjs-api npm install next-swagger-doc swagger-jsdoc -
编写 API 路由和文档注释:在你的 API 路由文件(如
pages/api/users.ts)中,使用 JSDoc 风格的注释来描述接口。// apps/nextjs-api/pages/api/users.ts import { NextApiRequest, NextApiResponse } from 'next'; import swaggerJsdoc from 'swagger-jsdoc'; // 定义 OpenAPI 规范的元数据 const options = { definition: { openapi: '3.0.0', info: { title: 'My App API', version: '1.0.0', }, }, apis: ['./pages/api/*.ts'], // 扫描所有 API 路由文件 }; const openapiSpecification = swaggerJsdoc(options); /** * @swagger * /api/users: * get: * summary: 获取所有用户列表 * tags: [Users] * responses: * 200: * description: 返回一个用户对象数组 * content: * application/json: * schema: * type: array * items: * type: object * properties: * id: * type: string * name: * type: string * email: * type: string */ export default function handler(req: NextApiRequest, res: NextApiResponse) { if (req.method === 'GET') { // 模拟从数据库获取数据 const users = [ { id: '1', name: 'Alice', email: 'alice@example.com' }, { id: '2', name: 'Bob', email: 'bob@example.com' }, ]; return res.status(200).json(users); } res.status(405).json({ message: 'Method Not Allowed' }); } -
创建生成脚本:编写一个脚本,在开发或构建时生成
openapi.json文件,并将其复制到共享的packages/api-spec目录。// apps/nextjs-api/scripts/generate-spec.ts import * as fs from 'fs'; import * as path from 'path'; import swaggerJsdoc from 'swagger-jsdoc'; const options = { definition: { openapi: '3.0.0', info: { title: 'My App API', version: '1.0.0' }, }, apis: ['./pages/api/*.ts'], }; const openapiSpecification = swaggerJsdoc(options); // 将生成的规范写入到共享包中 const specPath = path.join(__dirname, '../../packages/api-spec/openapi.json'); fs.writeFileSync(specPath, JSON.stringify(openapiSpecification, null, 2)); console.log('✅ OpenAPI specification generated successfully!'); -
更新
package.json:添加一个脚本来运行上述生成命令。// apps/nextjs-api/package.json { "scripts": { "dev": "next dev", "build": "npm run generate-spec && next build", "generate-spec": "ts-node scripts/generate-spec.ts" } }
第二步:配置 Flutter 客户端自动生成代码
Flutter 社区有强大的工具来根据 OpenAPI 规范生成代码,我们使用 openapi-generator。
-
安装 OpenAPI Generator:最简单的方式是通过
npm全局安装。npm install -g @openapitools/openapi-generator-cli -
配置生成器:在
apps/flutter-app目录下创建一个配置文件openapi-generator-config.json。// apps/flutter-app/openapi-generator-config.json { "packageName": "my_app_api_client", "pubName": "my_app_api_client", "pubVersion": "1.0.0", "pubDescription": "API client generated from OpenAPI spec", "pubAuthor": "Your Name" } -
创建代码生成脚本:编写一个 shell 脚本,该脚本会从共享的
packages/api-spec目录读取openapi.json,并生成 Dart 代码到lib/generated目录。#!/bin/bash # apps/flutter-app/scripts/generate-client.sh SPEC_FILE=../../packages/api-spec/openapi.json OUTPUT_DIR=lib/generated echo "🚀 Generating Flutter API client from $SPEC_FILE..." openapi-generator-cli generate \ -i $SPEC_FILE \ -g dart-dio \ -o $OUTPUT_DIR \ -c openapi-generator-config.json echo "✅ Flutter API client generated successfully in $OUTPUT_DIR!"请确保给这个脚本执行权限:
chmod +x apps/flutter-app/scripts/generate-client.sh。 -
更新
pubspec.yaml:添加一个脚本来方便地运行生成命令。# apps/flutter-app/pubspec.yaml name: flutter_app description: A new Flutter project. version: 1.0.0+1 environment: sdk: '>=3.0.0 <4.0.0' dependencies: flutter: sdk: flutter # 引入生成的 API 客户端包 my_app_api_client: path: lib/generated dev_dependencies: flutter_test: sdk: flutter flutter_lints: ^2.0.0 scripts: # 需要安装 flutter_scripts 或类似工具,或者直接用 shell 命令 generate-client: sh scripts/generate-client.sh
工作流:让一切自动运转
完成以上配置后,一个高效、无错的开发工作流就形成了:
- 后端开发:开发者在
apps/nextjs-api中修改或新增 API 路由,并更新相应的 JSDoc 注释。 - 生成契约:运行
npm run generate-spec(或在build时自动运行)。packages/api-spec/openapi.json文件被更新。 - 生成客户端:切换到
apps/flutter-app目录,运行npm run generate-client。lib/generated目录下的 Dart 代码被自动更新。 - 前端开发:Flutter 开发者现在可以直接使用新生成的、类型安全的 API 客户端代码进行开发,无需手动编写任何网络请求代码。
总结
通过在 Monorepo 中实施这种“契约优先”的策略,我们成功地解决了 Next.js 和 Flutter 之间的 API 同步难题。这不仅极大地提升了开发效率,减少了人为错误,还使得个人开发中前后端项目切换更加顺畅。后端更新后,移动端能立即获得最新、最准确的客户端代码,真正实现了高效的全栈开发体验。