-
Notifications
You must be signed in to change notification settings - Fork 357
Description
Bug: Prefix Transform Doesn't Strip Prefixes from Variable Types During Query Delegation
Description
When using createPrefixTransform in GraphQL Mesh, input types are correctly prefixed in the composed supergraph schema, but when queries are delegated to subgraphs, the variable type names in the query document retain the prefix, causing validation errors on the subgraph.
Environment
- @graphql-mesh/compose-cli: ^1.11.4
- @graphql-hive/gateway: ^2.3.1
- Node.js: v20.x
- GraphQL: 16.x
Reproduction
mesh.config.ts
import { createPrefixTransform, defineConfig, loadGraphQLHTTPSubgraph } from '@graphql-mesh/compose-cli';
export const composeConfig = defineConfig({
subgraphs: [
{
sourceHandler: loadGraphQLHTTPSubgraph('VendureShop', {
endpoint: 'http://localhost:3000/shop-api',
}),
transforms: [
createPrefixTransform({
value: 'vendureShop_',
includeRootOperations: true
}),
]
}
]
});Subgraph Schema (VendureShop)
input DateRange {
start: DateTime!
end: DateTime!
}
type Customer {
id: ID!
orders(options: OrderListOptions): OrderList!
}
input OrderListOptions {
filter: OrderFilterParameter
}
input OrderFilterParameter {
orderPlacedAt: DateOperators
}
input DateOperators {
between: DateRange
}Client Query
query GetRecentOrders($dateRange: vendureShop_DateRange) {
vendureShop_activeCustomer {
orders(
options: {
filter: {
orderPlacedAt: { between: $dateRange }
}
}
) {
items {
id
orderPlacedAt
}
}
}
}Variables
{
"dateRange": {
"start": "2026-01-01T00:00:00Z",
"end": "2026-02-01T00:00:00Z"
}
}Expected Behavior
The gateway should:
- Accept the query with
$dateRange: vendureShop_DateRange(prefixed type from supergraph) - When delegating to the VendureShop subgraph, rewrite the query to use
$dateRange: DateRange(unprefixed) - Successfully execute the query against the subgraph
Actual Behavior
The query sent to the subgraph contains:
query GetRecentOrders($dateRange: vendureShop_DateRange) { ... }The subgraph responds with:
{
"errors": [{
"message": "Unknown type \"vendureShop_DateRange\".",
"extensions": {
"code": "GRAPHQL_VALIDATION_FAILED"
}
}]
}Root Cause Analysis
The createPrefixTransform:
- ✅ Correctly transforms the schema by prefixing type definitions
- ✅ Correctly transforms field types and arguments in the schema
- ❌ Does NOT transform variable type references in query documents during delegation
When a query is delegated to a subgraph, the gateway sends the query document as-is with prefixed variable type names, which don't exist in the original subgraph schema.
Impact
This bug affects all queries using variables with prefixed input types, which includes:
- Filter parameters
- Sort options
- Pagination arguments
- Complex input objects
- Any custom input types
In our production system:
- 280 Vendure input types affected
- 47 Strapi input types affected
- 327 total input types that cannot be used in variables
This is a critical blocker for using prefix transforms with any real-world GraphQL API that uses input types.
Current Workaround
We're forced to avoid using variables with prefixed input types:
Workaround 1: Remove variables, limit results, filter client-side
# Instead of filtering with DateRange variable, fetch more and filter in JS
query GetRecentOrders {
vendureShop_activeCustomer {
orders(
options: {
sort: { orderPlacedAt: DESC }
take: 100 # Fetch recent orders
}
) {
items {
id
orderPlacedAt
}
}
}
}// Filter in application code
const sevenDaysAgo = new Date(Date.now() - 7 * 24 * 60 * 60 * 1000);
const filtered = orders.items.filter(order =>
new Date(order.orderPlacedAt) >= sevenDaysAgo
);Problems:
- Inefficient (fetches unnecessary data)
- Doesn't scale (limited to max page size)
- Moves filtering from database to application layer
- Increases network payload and latency
Workaround 2: Inline values (loses variable benefits)
query GetRecentOrders {
vendureShop_activeCustomer {
orders(
options: {
filter: {
orderPlacedAt: {
between: {
start: "2026-01-01T00:00:00Z"
end: "2026-02-01T00:00:00Z"
}
}
}
}
) {
items { id }
}
}
}Problems:
- Can't use dynamic values
- Breaks query caching
- Requires string manipulation/injection
- Security concerns with user input
Proposed Solution
The prefix transform should intercept query delegation and rewrite variable type names:
// Pseudo-code for the fix
class PrefixTransform {
transformRequest(request, delegationContext) {
if (delegationContext.targetSubgraph === this.subgraphName) {
// Parse query document
// Find all variable definitions
// Replace prefixed type names with original names
const fixedQuery = rewriteVariableTypes(
request.document,
this.prefix,
'strip' // Remove prefix when delegating to subgraph
);
return { ...request, document: fixedQuery };
}
return request;
}
}The transform should:
- Detect when delegating to the source subgraph
- Parse variable definitions in the query
- Strip the prefix from variable type names
- Preserve the prefix in field selections and everywhere else
Example of Expected Transformation
Query sent to gateway (from client):
query GetOrders($dateRange: vendureShop_DateRange) {
vendureShop_activeCustomer {
orders(options: { filter: { orderPlacedAt: { between: $dateRange }}}) {
items { id }
}
}
}Query delegated to subgraph (after transformation):
query GetOrders($dateRange: DateRange) { # ← Prefix stripped from variable type
activeCustomer { # ← Prefix stripped from field names
orders(options: { filter: { orderPlacedAt: { between: $dateRange }}}) {
items { id }
}
}
}Related Issues
- Similar issue in Apollo Federation was fixed in v2.0
- GraphQL Tools
wrapSchemahas similar challenges with variable types - This affects any schema transformation that renames types
Additional Context
This bug makes createPrefixTransform unusable for production systems that need to:
- Use multiple subgraphs with overlapping type names
- Pass complex filter/sort/pagination parameters
- Maintain efficient database queries
- Follow GraphQL best practices (variables for dynamic values)
We're currently evaluating implementing a custom transform workaround.