如何将现有GraphQL Node.js后端整合到Next.js应用中
整合方案说明
直接把现有服务端代码全部放入src/pages/api目录的做法不可行。Next.js的pages/api目录是为单文件Serverless接口设计的,每个文件对应一个独立的路由处理逻辑,和你当前基于Express+Apollo搭建的长驻运行式服务逻辑不匹配,直接迁移会出现数据库连接重复初始化、Apollo中间件挂载失败、冷启动性能极差等问题。
你要实现单仓库统一管理、代码共享、简化部署的目标,有两种成熟方案可选,改造成本都不高:
方案1:分目录Workspace结构(最适配现有代码,改造成本最低)
这个方案不需要修改你现有服务端的核心逻辑,是最稳妥的选择:
- 调整目录结构如下:
项目根目录/ ├── package.json # 根目录统一管理依赖、执行脚本 ├── shared/ # 存放前后端共享代码,比如GraphQL类型定义、通用工具函数、校验规则 ├── server/ # 原服务端所有代码完整迁移到这里,核心逻辑不用改 │ ├── src/ │ ├── data-source.ts │ └── resolvers/ └── client/ # 原Next.js客户端所有代码完整迁移到这里 ├── pages/ ├── src/ └── next.config.js
- 根目录
package.json开启yarn workspace能力,配置统一执行脚本,不用再开多个终端分别启动前后端:
{ "private": true, "workspaces": ["server", "client", "shared"], "scripts": { "dev": "concurrently \"yarn dev:server\" \"yarn dev:client\"", "dev:server": "yarn workspace server dev", "dev:client": "yarn workspace client dev", "build": "yarn workspace server build && yarn workspace client build", "start": "concurrently \"yarn start:server\" \"yarn start:client\"", "typeorm": "yarn workspace server typeorm" }, "devDependencies": { "concurrently": "^8.0.0" } }
- 服务端代码只需要微调CORS配置即可,生产环境如果用Nginx等反向代理把
/graphql路径转发到服务端端口、其余路径转发到Next.js服务,甚至可以完全移除CORS配置。前后端需要共享的代码直接放在shared目录导入即可,不需要单独发布npm包。 - 部署时既可以选择前后端分开部署,也可以通过反向代理整合为同域名服务,流程比维护两个独立仓库简单很多。
方案2:Apollo挂载到Next.js API路由(适合Serverless部署场景)
如果你不想单独维护独立的Express服务端口,想完全依托Next.js的服务能力运行GraphQL接口,不要全量迁移代码到pages/api,只需要做单路由适配即可:
- 服务端的resolver、数据源配置等核心逻辑可以放在项目任意目录,不需要放到
pages/api下 - 新建
pages/api/graphql.ts作为Apollo服务的唯一入口,用Next.js适配包对接路由,不再自己调用app.listen启动独立端口 - 给TypeORM数据源加全局连接复用逻辑,避免Serverless场景下每次请求重复初始化数据库连接
入口代码参考:
import { ApolloServer } from '@apollo/server'; import { startServerAndCreateNextHandler } from '@as-integrations/next'; import "reflect-metadata"; import { buildSchema } from "type-graphql"; import { appDataSource } from "../../server/data-source"; import { HelloResolver, MarketDataResolver, UserResolver} from "../../server/resolvers"; // 全局复用数据库连接 let dsInited = false; if (!dsInited) { await appDataSource.initialize(); await appDataSource.runMigrations(); dsInited = true; } const server = new ApolloServer({ schema: await buildSchema({ resolvers: [UserResolver, HelloResolver, MarketDataResolver], validate: false, }), }); export default startServerAndCreateNextHandler(server, { context: () => ({ em: appDataSource.manager }), });
- 这个方案下GraphQL接口默认运行在
http://localhost:3000/api/graphql,不需要单独监听4000端口,客户端修改请求地址即可,开发时只需要启动Next.js服务就行。 - 如果你的服务端包含定时任务、长连接、大流量批处理等逻辑,不要选这个方案,Serverless环境对这类长驻逻辑支持很差,选方案1更合适。
原有启动方式调整
- 用方案1的话,根目录执行
yarn dev就会同时启动服务端的ts监听、nodemon重启和客户端Next.js服务,不需要手动开两个终端操作 - 用方案2的话,直接执行Next.js的dev启动命令即可,不需要单独启动服务端进程
内容的提问来源于stack exchange,提问作者sev
相关产品推荐
相关产品推荐

