graphql-tools icon indicating copy to clipboard operation
graphql-tools copied to clipboard

stitchSchemas with namespaced queries and mutations

Open wmwart opened this issue 3 years ago • 3 comments
trafficstars

Discussed in https://github.com/ardatan/graphql-tools/discussions/4042

Originally posted by wmwart December 22, 2021 Good afternoon. I have a question about using stitchSchemas with namespaced queries and mutations. I have 2 remote services:

###AssetService
type Asset {
  id: ID!
  name: String!
  description: String
  ...
}

type AssetPayload {
  item: Asset
}

type AssetsPayload {
  items: [Asset!]
  pageInfo: PaginationInfo
}

input AssetWhereUniqueInput {
  id: ID
}

type AssetQuery {
  getOne(where: AssetWhereUniqueInput!): AssetPayload! @withAuth
  getList(filter: AssetFilter = {} sort: [AssetSort! ] = [{id: asc}] page: Int = 1 perPage: Int = 100): AssetsPayload! @withAuth
  getMany(ids: [ID!]!): AssetsPayload! @withAuth
}

type AssetMutation {
  create(input: AssetCreateInput!): AssetPayload! @withAuth
  update( where: AssetWhereUniqueInput! input: AssetUpdateInput!): AssetPayload! @withAuth
  remove(where: AssetWhereUniqueInput!): AssetPayload! @withAuth
  restore(where: AssetWhereUniqueInput!): AssetPayload! @withAuth
  delete(where: AssetWhereUniqueInput!): AssetPayload! @withAuth
}

type Query {
  Asset: AssetQuery
}

type Mutation {
  Asset: AssetMutation
}

...

and

### AssetClassService
type AssetClass {
  id: ID!
  name: String!
  assets: [AssetWithoutAssetClass!]!
}

type AssetClassPayload {
  item: AssetClass
}

type AssetClassesPayload {
  items: [AssetClass!]
  pageInfo: PaginationInfo
}

type AssetClassQuery {
  getOne(where: AssetClassWhereUniqueInput!): AssetClassPayload! @withAuth
  getList(filter: AssetClassFilter = {} sort: [AssetClassSort! ] = [{id: asc}] page: Int = 1 perPage: Int = 100): AssetClassesPayload! @withAuth
  getMany(ids: [ID!]): [AssetClass!]!
}

type AssetClassMutation {
  create(input: AssetClassCreateInput!): AssetClassPayload! @withAuth
  update( where: AssetClassWhereUniqueInput! input: AssetClassUpdateInput!): AssetClassPayload! @withAuth
  remove(where: AssetClassWhereUniqueInput!): AssetClassPayload! @withAuth
  restore(where: AssetClassWhereUniqueInput!): AssetClassPayload! @withAuth
  delete(where: AssetClassWhereUniqueInput!): AssetClassPayload! @withAuth
}

type Asset {
  id: ID!
  assetClass: AssetClass
}


type AssetQuery {
  getMany(ids: [ID!]!): [Asset!]!
}


type Query {
  AssetClass: AssetClassQuery
  Asset: AssetQuery 
}

type Mutation {
  AssetClass: AssetClassMutation
}

Thus, when using stitchSchemas in my proxy service, a function for obtaining a stitched schema appeared:

const makeGatewaySchema = async () => {

  const assetServiceExecutor = makeRemoteExecutor('http://localhost:7702', { log: true });
  const assetClassServiceExecutor = makeRemoteExecutor('http://localhost:7703', { log: true });

  return stitchSchemas({
    subschemas: [
      {
        schema: await introspectSchema(assetServiceExecutor),
        executor: assetServiceExecutor,
        merge: {...}
      },
      {
        schema: await introspectSchema(assetClassServiceExecutor),
        executor: assetClassServiceExecutor,
        merge: {...}
      },
    ],
  });
}

Which allows me to fulfill the request:

query Items {
  Asset {
    getList {
      items {
        id
        name
        assetClass {
          id
          name
          description
        }
      }
    }
  }
}

However, none of the combination of merge options of these subcircuits specified in the documentation and examples of use returned me the required answer. I assume that the problem is precisely in the namespaced types.

Please help me specify the correct settings for merge options or share a link to a solution to a similar problem

wmwart avatar Dec 23 '21 06:12 wmwart

I used different options for describing fieldName with merge AssetClass from AssetClassService: fieldName: 'AssetClass.getMany', fieldName: { AssetClass: "getMany"}, fieldName: "getMany", and other. But nothing happened

In addition, the query that I indicated above gave the result without additional merge options, but except for the assetClass {..., assetClass: null} field.

However, when I added to the AssetClassService in the schema

type AssetQuery {
  getMany(ids: [ID!]): AssetsPayload! @withAuth
}

type Query {
  Asset: AssetQuery
}

The request stopped returning data.

{
  "data": {
    "Asset": null
  }
}

test various options ...

return stitchSchemas({
    subschemas: [
      {
        schema: await introspectSchema(assetServiceExecutor),
        executor: assetServiceExecutor,

        merge: {
          Asset: {
            canonical: true,
            selectionSet: '{ id }',
            fieldName: 'Asset.getList',
            args: ({ id }) => {

              return { id }
            },
          },
          AssetClass: {
            selectionSet: '{ id }',
            fieldName: 'getMany',
            key: (root, args, context, info) => {

              return root.id
            },
            argsFromKeys: (root, args, context, info) => {

              return { ids: root.ids }
            },
          }
        }
      },
      {
        schema: await introspectSchema(assetClassServiceExecutor),
        executor: assetClassServiceExecutor,
        batch: true,
        merge: {
          Asset: {
            selectionSet: '{ id assets }',
            fieldName: 'Asset._resolveReference',
            key: (root, args, context, info) => {

              return root.assets
            },
            argsFromKeys: (root, args, context, info) => {

              return { ids: root.assets }
            },
          },
          AssetClass: {
            canonical: true,
            selectionSet: '{ id }',
            fieldName: 'AssetClass.getMany',
            key: (root, args, context, info) => {

              return root.id
            },
            argsFromKeys: (root, args, context, info) => {

              return { ids: root.ids }
            },
          }
        }
      },
    ],
  });
}

wmwart avatar Dec 23 '21 08:12 wmwart

Thanks for reporting this issue. Would you create a PR with a failing test?

ardatan avatar Jan 04 '22 14:01 ardatan

@ardatan , Here is a repository with two examples: https://github.com/denisgnt/gateway-service-example.

wmwart avatar Jul 25 '22 12:07 wmwart