Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature Request: Operation Versioning #7882

Open
afigard opened this issue Nov 4, 2024 · 1 comment
Open

Feature Request: Operation Versioning #7882

afigard opened this issue Nov 4, 2024 · 1 comment

Comments

@afigard
Copy link
Contributor

afigard commented Nov 4, 2024

Context

In complex API ecosystems, different versions of operations are often required to support backward compatibility and evolution. This feature request aims to support operation versioning in GraphQL Mesh.

Currently, GraphQL Mesh merges all versions of an operation into a single query, which can result in conflicts and loss of version-specific details. By enabling operation versioning, each version of an operation will be exposed as a distinct query in the generated supergraph. This allows clients to explicitly request specific API versions, ensuring compatibility and leveraging new features without breaking existing functionality.

Motivational Example

Given the following swaggers:
suppliers_v1.json;

{
  "openapi": "3.0.0",
  "info": {
    "title": "Suppliers API",
    "version": "1.0.0"
  },
  "paths": {
    "/suppliers/{id}": {
      "get": {
        "operationId": "getSupplierById",
        "summary": "Get a supplier by ID",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "The supplier ID"
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Supplier"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "Supplier": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer"
          },
          "name": {
            "type": "string"
          }
        }
      }
    }
  }
}

suppliers_v2.json;

{
  "openapi": "3.0.0",
  "info": {
    "title": "Suppliers API",
    "version": "2.0.0"
  },
  "paths": {
    "/suppliers/{id}": {
      "get": {
        "operationId": "getSupplierById",
        "summary": "Get a supplier by ID",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "The supplier ID"
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Supplier"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "Supplier": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "address": {
            "type": "string"
          }
        }
      }
    }
  }
}

And the following mesh.config.ts file;

import { defineConfig } from "@graphql-mesh/compose-cli";
import { loadOpenAPISubgraph } from "@omnigraph/openapi";

export const composeConfig = defineConfig({
  subgraphs: [
    {
      sourceHandler: loadOpenAPISubgraph("suppliers@1", {
        source: "./suppliers_v1.json",
      }),
    },
    {
      sourceHandler: loadOpenAPISubgraph("suppliers@2", {
        source: "./suppliers_v2.json",
      }),
    },
  ],
});

The supergraph.graphql output by Mesh will only have one query: getSupplierById.

We propose to add a operationVersioning parameter to the defineConfig, so that, by using it in the mesh.config.ts file like this;

import { defineConfig } from "@graphql-mesh/compose-cli";
import { loadOpenAPISubgraph } from "@omnigraph/openapi";

export const composeConfig = defineConfig({
  subgraphs: [
    {
      sourceHandler: loadOpenAPISubgraph("suppliers@1", {
        source: "./suppliers_v1.json",
      }),
    },
    {
      sourceHandler: loadOpenAPISubgraph("suppliers@2", {
        source: "./suppliers_v2.json",
      }),
    },
  ],
  operationVersioning: true,
});

The supergraph.graphql output by Mesh will now have two queries: getSupplierById_v1 and getSupplierById_v2 which will respectively return the distinct objects Supplier_v1 and Supplier_v2.

@ardatan
Copy link
Owner

ardatan commented Nov 4, 2024

GraphQL Mesh will automatically try to merge types and fields. In order to prevent conflicts, transforms can help in my opinion;
https://the-guild.dev/graphql/mesh/v1/transforms/prefix
But let me know if I miss something with your case.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants