ardatan / graphql-mesh

🕸️ GraphQL Federation Framework for any API services such as REST, OpenAPI, Swagger, SOAP, gRPC and more...
https://the-guild.dev/graphql/mesh
MIT License
3.3k stars 347 forks source link

Feature Request: Lenient Merging #7883

Open afigard opened 2 weeks ago

afigard commented 2 weeks ago

Context

In complex API ecosystems, it is common to encounter inconsistencies between specifications from different sources. Such discrepancies can lead to conflicts and complications when attempting to integrate these APIs into a unified supergraph with GraphQL Mesh.

To address these challenges, we propose a feature request for GraphQL Mesh that would introduce an option for more "flexible" or lenient merging of types. This feature would enable us to bypass the inconsistencies across our various Swagger specifications, allowing for smoother integration and usage of our APIs.

Motivational Example

"Person" definition in a 1st Swagger;

"Person": {
    "type": "object",
    "discriminator": {
        "propertyName": "personType",
        "mapping": {
            "ENTERPRISE": "#/components/schemas/Enterprise",
            "INDIVIDUAL": "#/components/schemas/Individual"
        }
    },
    "properties": {
        "id": {
            "type": "string"
        },
        "accountAccess": {
            "type": "array",
            "items": {
                "type": "string"
            }
        },
        "personType": {
            "type": "string"
        }
    }
}

"Person" definition in a 2nd Swagger;

"Person": {
    "type": "object",
    "properties": {
        "id": {
            "type": "string"
        },
        "login": {
            "type": "array",
            "items": {
                    "type": "string"
            }
        }
    }
}

Result (Mesh Compose ERROR);

➡️ Type "Person" has mismatched kind: it is defined as Interface Type in subgraph "A" but Object Type in subgraph "B"

In this case, we would like to have, in our supergraph.graphql file output by Mesh, a "Person" interface with 4 fields: id, accountAccess, personType and login.

Here are some other inconsistency errors we would also like to solve with a more flexible merging of type;

➡️ Input object field "x" is required in some subgraphs but does not appear in all subgraphs: it is required in subgraph "A" but does not appear in subgraph "B"

➡️ Type of field "x.y" is incompatible across subgraphs: it has type "a" in subgraph "A" but type "b" in subgraph "B"

ardatan commented 2 weeks ago

Type of field "x.y" is incompatible across subgraphs: it has type "a" in subgraph "A" but type "b" in subgraph "B"

You can rename that field in one of the subgraphs to resolve the conflict.

Input object field "x" is required in some subgraphs but does not appear in all subgraphs: it is required in subgraph "A" but does not appear in subgraph "B"

The best option would be to rename one of the input object types to avoid merging those input objects.

In this case, we would like to have, in our supergraph.graphql file output by Mesh, a "Person" interface with 4 fields: id, accountAccess, personType and login.

If they are the same entities, you can transform Person in the other one to be an interface object using the following transform; https://the-guild.dev/graphql/mesh/v1/transforms/federation

 createFederationTransform({
          // Schema Coordinates
          Product: {
             interfaceObject: true
          }
})
graham-iheart commented 1 week ago

Type of field "x.y" is incompatible across subgraphs: it has type "a" in subgraph "A" but type "b" in subgraph "B"

You can rename that field in one of the subgraphs to resolve the conflict.

Would using encapsulation work for this?

ardatan commented 1 week ago

I'd recommend to avoid using it to be honest, because it breaks the query delegation so you won't be able to merge types from different sources and stitch fields from a subgraph to another one when you use it. I'd use rename or prefix transform