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

Apollo联邦中继承的内部GraphQL实体解析为null问题求助

Apollo联邦+Graphene内部Schema无法解析关联实体问题

场景说明

使用Django+Graphene+graphene-federation搭建Apollo联邦,整合car-service和race-service两个GraphQL服务,每个服务提供/internal/graphql/和/external/graphql/两个端点。内部Schema的类型继承自外部类型(如CarTypeInternal继承CarTypeExternal),但内部API查询时关联的车辆实体无法被解析。

代码片段

通用urls.py

urlpatterns = [
    path(
        "internal/graphql/",
        csrf_exempt(
            GraphQLView.as_view(
                graphiql=True,
                schema=internal_schema,
            )
        ),
    ),
    path(
        "external/graphql/",
        csrf_exempt(
            GraphQLView.as_view(
                graphiql=True,
                schema=external_schema,
            )
        ),
    ),
]

car-service external_schema.py

@key(fields="uuid")
class CarTypeExternal(django_filters.DjangoObjectType):
    class Meta:
        model = models.Car
        interfaces = (graphene.relay.Node,)

    def __resolve_reference(self, info, **kwargs):
        query_params = {
            "owner_uuid": get_user_by_token(info.context),
            "uuid": self.uuid
        }
        try:
            return models.Car.objects.filter(**query_params).first()
        except models.Car.DoesNotExist:
            return None

car-service internal_schema.py

@key(fields="uuid")
class CarTypeInternal(external_schema.CarTypeExternal):
    class Meta:
        model = models.Car
        interfaces = (graphene.relay.Node,)

    def __resolve_reference(self, info, **kwargs):
        query_params = {
            "uuid": self.uuid
        }
        try:
            return models.Car.objects.filter(**query_params).first()
        except models.Car.DoesNotExist:
            return None

race-service external_schema.py

@extend(fields="uuid")
class CarTypeExternal(graphene.ObjectType):
    uuid = external(graphene.UUID(required=True))


class RaceTypeExternal(django_filters.DjangoObjectType):
    cars = graphene.List(CarTypeExternal)

    class Meta:
        model = models.Race
        interfaces = (graphene.relay.Node,)

    def resolve_cars(self, info, **kwargs):
        cars = []
        for race_car in models.RaceCar.objects.filter(race__uuid=self.uuid):
            if race_car.car_uuid is not None:
                cars.append(
                    CarTypeExternal(
                        uuid=race_car.car_uuid
                    )
                )
        return cars


class Query(graphene.ObjectType):
    race = graphene.Field(
        RaceTypeExternal,
        uuid=graphene.UUID(required=True),
    )

    @staticmethod
    def resolve_race(self, info, uuid):
        return models.Race.objects.get(
            uuid=uuid,
        )

race-service internal_schema.py

@extend(fields="uuid")
class CarTypeInternal(graphene.ObjectType):
    uuid = external(graphene.UUID(required=True))


class RaceTypeInternal(RaceTypeExternal):
    cars = graphene.List(CarTypeInternal)

    class Meta:
        model = models.Race
        interfaces = (graphene.relay.Node,)

    def resolve_cars(self, info, **kwargs):
        cars = []
        for race_car in models.RaceCar.objects.filter(race__uuid=self.uuid):
            if race_car.car_uuid is not None:
                cars.append(
                    CarTypeInternal(
                        uuid=race_car.car_uuid
                    )
                )
        return cars


class Query(graphene.ObjectType):
    race = graphene.Field(
        RaceTypeInternal,
        uuid=graphene.UUID(required=True),
    )

    @staticmethod
    def resolve_race(self, info, uuid):
        driver_uuid = get_user_by_token(info.context)
        return models.Race.objects.get(
            uuid=uuid,
            driver_uuid=driver_uuid,
        )

查询结果

  • 外部API查询(正常返回cars列表):
{
  "race": {
    "uuid": "7ac2b50f-5db8-4425-9b59-0a714a028ea0",
    "name": "Nurburgring",
    "cars": [{
        "uuid": "3ba0c10f-0553-4565-a208-2c45d23f9145",
        "name": "Audi"
      },
      {
        "uuid": "c6532341-b521-4c59-8536-af06d47e7b2a",
        "name": "BMW"
      }
    ]
  }
}
  • 内部API查询同一场race(cars字段为null):
{
  "race": {
    "uuid": "7ac2b50f-5db8-4425-9b59-0a714a028ea0",
    "name": "Nurburgring",
    "cars": null
  }
}
  • 内部API查询无关联车辆的race(cars字段为[]):
{
  "race": {
    "uuid": "b92098f7-2d2d-4f6d-a538-1f74f83fce27",
    "name": "Car track",
    "cars": []
  }
}

问题分析与修复方案

1. 修复代码笔误

race-service的resolve_cars方法原代码返回未定义的analysis_runs变量,属于明显笔误,修正为返回构造好的cars列表。这个错误直接导致有车辆关联时返回值异常,修正后可解决null问题。

2. 统一联邦实体标识

race-service内部的CarTypeInternal需要和car-service内部的CarTypeInternal使用一致的实体名称,避免联邦无法识别为同一实体。可通过@extend装饰器显式指定name参数:

@extend(fields="uuid", name="Car")
class CarTypeInternal(graphene.ObjectType):
    uuid = external(graphene.UUID(required=True))

3. 配置网关识别内部服务端点

Apollo网关需单独配置内部服务的端点,确保内部Schema被纳入联邦。示例配置:

serviceList:
  - name: "car-service-internal"
    url: "http://car-service/internal/graphql/"
  - name: "race-service-internal"
    url: "http://race-service/internal/graphql/"

4. 验证内部Schema的实体注册

确保car-service的内部Schema正确注册CarTypeInternal并生成联邦Schema:

# car-service internal_schema.py 末尾
schema = build_schema(Query, types=[CarTypeInternal])

5. 权限与数据排查

检查内部API的resolve_race方法中,当前请求用户是否拥有目标race的权限;同时单独调用car-service内部端点,确认关联车辆能被正常返回。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.06 12:15:41