You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

两个独立域名NextJS应用调用同一GraphQL后端的CORS问题求解

跨域问题解决方案:两个NextJS应用共享GraphQL后端

问题背景

我有两个分属不同代码仓库、使用不同域名的NextJS应用,希望第二个应用(应用B)调用第一个应用(应用A)的GraphQL+Apollo后端,但因域名不匹配遇到了Access-Control-Allow-Origin跨域问题。

架构说明

两个UI、两个域名、一个后端(架构示意图如下):
架构示意图

环境差异

  • 本地环境运行正常:
    • 应用A:localhost:3000
    • 应用B:localhost:4000
  • 线上环境切换域名后出现CORS问题,应用B的NEXT_PUBLIC_APOLLO_BASE_URL配置为应用A的线上域名。

Apollo客户端配置代码

import {
  ApolloClient,
  ApolloClientOptions,
  InMemoryCache,
  NormalizedCacheObject,
  createHttpLink,
} from '@apollo/client'
import { auth } from 'app/firebase/clientApp'
import { setContext } from '@apollo/client/link/context'

const apolloCache = new InMemoryCache({
  typePolicies: {
    SearchConfigOption: {
      keyFields: ['pathName'],
    },
    Query: {
      fields: {
        /* filterItems: {
          keyArgs: ['user'],
          merge(existing, incoming) {
            if (!incoming) return existing
            if (!existing) return incoming // existing will be empty the first time

            const { items, ...rest } = incoming

            let result = rest
            result.items = [...existing.items, ...items] // Merge existing items with the items from incoming

            return result
          },
        },*/
      },
    },
  },
})

const httpLink = createHttpLink({
  uri: `${process.env.NEXT_PUBLIC_APOLLO_BASE_URL}api/graphql`,
  credentials: 'include',
})

const authLink = setContext(async (_, { headers }) => {
  const user = auth.currentUser
  const token = user && (await user.getIdToken())
  const modifiedHeader = {
    headers: {
      ...headers,
      authorization: token ? `Bearer ${token}` : 'Bearer null',
    },
  }
  return modifiedHeader
})

const client = new ApolloClient({
  ssrMode: typeof window === 'undefined',
  link: authLink.concat(httpLink),
  cache: apolloCache,
  connectToDevTools: process.env.NODE_ENV !== 'production',
  credentials: 'include',
})

export default client

最佳解决方案

1. 在应用A的NextJS后端配置CORS白名单

这是最直接的解决方式,在应用A的GraphQL API路由中添加CORS规则,明确允许应用B的域名访问。

实现步骤:

  • 在应用A的pages/api/graphql.ts(或对应路由文件)中,引入cors库并配置:
    import Cors from 'cors'
    import { ApolloServer } from '@apollo/server'
    import { startServerAndCreateNextHandler } from '@as-integrations/next'
    
    // 初始化CORS配置
    const cors = Cors({
      origin: process.env.NEXT_PUBLIC_APP_B_DOMAIN, // 应用B的线上域名,例如"https://app-b.example.com"
      credentials: true, // 匹配Apollo客户端的credentials: 'include'配置
    })
    
    // 包装Apollo请求处理逻辑,先执行CORS校验
    const handler = startServerAndCreateNextHandler(server, {
      context: async (req, res) => {
        await new Promise((resolve, reject) => {
          cors(req, res, (result) => {
            if (result instanceof Error) return reject(result)
            resolve(result)
          })
        })
        return { req, res }
      },
    })
    
    export default handler
    
  • 注意:生产环境禁止使用*作为origin(会和credentials: true冲突),如果需要允许多个域名,可传入数组:origin: ['https://app-b.example.com', 'https://app-c.example.com']。

2. 应用B侧配置NextJS重写代理请求

通过应用B的重写规则,将GraphQL请求代理到应用A的域名,浏览器端请求会指向应用B自身域名,规避跨域限制。

实现步骤:

  • 在应用B的next.config.js中添加重写规则:
    /** @type {import('next').NextConfig} */
    const nextConfig = {
      async rewrites() {
        return [
          {
            source: '/api/graphql', // 前端请求该路径
            destination: `${process.env.NEXT_PUBLIC_APOLLO_BASE_URL}/api/graphql`, // 代理到应用A的接口
          },
        ]
      },
    }
    
    module.exports = nextConfig
    
  • 修改应用B的Apollo客户端uri为自身路径:
    const httpLink = createHttpLink({
      uri: '/api/graphql', // 不再直接写应用A的域名
      credentials: 'include',
    })
    
  • 优势:无需修改应用A代码,跨域处理完全在应用B内部完成,适合无法改动应用A配置的场景。

3. 剥离独立GraphQL后端(长期最佳实践)

如果未来有更多应用需要调用该GraphQL服务,建议将后端从应用A中剥离,部署为独立服务(例如使用https://api.example.com域名),统一配置CORS允许所有需要的前端域名访问。

核心优势:

  • 架构解耦,后端服务与前端应用独立部署、维护;
  • 便于统一管理API权限、监控和版本迭代;
  • 避免单个前端应用的部署影响后端服务稳定性。

4. 本地开发环境补充(可选)

若需模拟线上跨域场景,可在应用B的next.config.js中添加开发环境重写:

const nextConfig = {
  async rewrites() {
    if (process.env.NODE_ENV === 'development') {
      return [
        {
          source: '/api/graphql',
          destination: 'http://localhost:3000/api/graphql',
        },
      ]
    }
    return []
  },
}

内容的提问来源于stack exchange,提问作者Mona Fang

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.20 22:32:03