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

Aliases do not work #15

Open
cozmo opened this issue Aug 26, 2021 · 3 comments
Open

Aliases do not work #15

cozmo opened this issue Aug 26, 2021 · 3 comments

Comments

@cozmo
Copy link

cozmo commented Aug 26, 2021

Using the example, the following query works

query {
  findProduct {
    id
    name
  }
}
{
  "data": {
    "findProduct": {
      "id": "123",
      "name": "name from transformed service"
    }
  }
}

however this query with an alias does not work

query {
  findProduct {
    id
    aliasedName: name
  }
}
{
  "errors": [
    {
      "message": "Cannot return null for non-nullable field Product.name.",
      "path": [
        "_entities",
        0,
        "aliasedName"
      ],
      "extensions": {
        "code": "INTERNAL_SERVER_ERROR",
        "serviceName": "transformed",
        "query": "query($representations:[_Any!]!){_entities(representations:$representations){...on Product{aliasedName:name}}}",
        "variables": {
          "representations": [
            {
              "__typename": "Product",
              "id": "123"
            }
          ]
        },
      }
    }
  ],
  "data": null
}

I've dug into this a fair amount, and as best as I can tell, this comes because in the course of satisfying this query, the "transformed" data source is queried "twice". The federation server slices up the query and sends the entity query to the transformed subgraph

query($representations: [_Any!]!) {
  _entities(representations: $representations) {
    ... on Product {
      aliasedName: name
    }
  }
}

(This query has the alias, as one would expect). This query then gets handled by the schema that transformSchemaFederation creates. The relevant parts of the query (including aliases) get sent to resolveReference, which then executes them against the subgraph server. The response from delegateToSchema is

{
  "aliasedName": "name from transformed service"
}

This is where the error arises from, because the query that the federation server made (see above) fulfilled by applying it against the subschema. When the subschema returns with the aliased field names, the query made against it fails since the shape of the data doesn't match the expected shape (missing name).

I think to do this "right" this library needs to transform the query and data instead of using deletateToSchema and effectively "double querying"? But maybe there's an easier fix I'm missing?

@cozmo
Copy link
Author

cozmo commented Aug 26, 2021

An alternatively (very hacky) solution is to just strip the aliases out before querying the underlying service with delegateToSchema

diff --git a/example/transformed-server.ts b/example/transformed-server.ts
index 32d89db..9f0c127 100644
--- a/example/transformed-server.ts
+++ b/example/transformed-server.ts
@@ -1,6 +1,7 @@
 import { delegateToSchema, makeExecutableSchema } from 'graphql-tools';
 import { ApolloServer } from 'apollo-server';
 import { transformSchemaFederation } from '../src/transform-federation';
+import { visit } from 'graphql';
 
 const products = [
   {
@@ -54,7 +55,35 @@ const federationSchema = transformSchemaFederation(schemaWithoutFederation, {
           id: (reference as ProductKey).id,
         },
         context,
-        info,
+        info: {
+          ...info,
+          fieldNodes: info.fieldNodes.map((n) =>
+            visit(n, {
+              Field: (field) => {
+                if (!field.alias) {
+                  return undefined;
+                }
+                return {
+                  ...field,
+                  alias: undefined,
+                };
+              },
+            })
+          ),
+          operation: visit(info.operation, {
+            Field: (field) => {
+              if (!field.alias) {
+                return undefined;
+              }
+              return {
+                ...field,
+                alias: undefined,
+              };
+            },
+          }),
+        },
       });
     },
   },

One thing that I'm not sure of is how this plays with fields with arguments, such as

type Name {
  name: String!
}

type Product {
  id: String!
  name(capitalize: Boolean!): Name
}

type Query {
  productById(id: String!): Product!
}

People write queries such as

query {
  findProduct {
    id
    upperCaseName: name(capitalize: true) {
      name
    }
    lowerCaseName: name(capitalize: false) {
      name
    }
  }
}

but I don't really understand how that would interplay with this (would the Name queries be handled by their own delegateToSchema?

@cozmo
Copy link
Author

cozmo commented Aug 30, 2021

I think to do this "right" this library needs to transform the query and data instead of using deletateToSchema and effectively "double querying"?

For what it's worth I implemented this approach here hasura/graphql-engine#3064 (comment), a lot of the schema/sdl translation was directly inspired by this library, but the query approach is completely different, and avoids the pitfalls of using deletateToSchema. Would be happy to figure out a way to bring this approach into this library.

@nikunjy
Copy link

nikunjy commented Mar 10, 2023

@0xR are there any plans to address this ? wdyt

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