apollo-tooling icon indicating copy to clipboard operation
apollo-tooling copied to clipboard

Incomplete (and duplicate) GraphQLResolverMap typings

Open ooflorent opened this issue 5 years ago • 2 comments

Intended outcome:

GraphQLResolverMap should accept enum and scalars. The following resolver should not produce any TypeScript error:

enum SomeEnum {
  FOO = "foo",
  BAR = "bar",
}

const AnotherEnum = {
  FOO: "foo",
  BAR: "bar",
};

const resolvers: GraphQLResolverMap<any> = {
  SomeEnum,
  AnotherEnum,
};

Actual outcome:

Works fine when using GraphQLResolverMap from apollo-graphql but does not when importing from @apollographql/apollo-tools.

GraphQLResolverMap is defined within apollo-graphql and @apollographql/apollo-tools but the definitions are not the same.

// apollo-graphql/src/schema/resolverMap.ts
export interface GraphQLResolverMap<TContext = {}> {
  [typeName: string]:
    | {
        [fieldName: string]:
          | GraphQLFieldResolver<any, TContext>
          | {
              requires?: string;
              resolve: GraphQLFieldResolver<any, TContext>;
            };
      }
    | GraphQLScalarType
    | {
        [enumValue: string]: string | number;
      };
}
// apollo-tools/src/schema/resolverMap.ts
export interface GraphQLResolverMap<TContext> {
  [typeName: string]: {
    [fieldName: string]:
      | GraphQLFieldResolver<any, TContext>
      | {
          requires?: string;
          resolve: GraphQLFieldResolver<any, TContext>;
          subscribe?: undefined;
        }
      | {
          requires?: string;
          resolve?: undefined;
          subscribe: GraphQLFieldResolver<any, TContext>;
        }
      | {
          requires?: string;
          resolve: GraphQLFieldResolver<any, TContext>;
          subscribe: GraphQLFieldResolver<any, TContext>;
        };
  };
}

This is problematic since apollo-server uses the definitions from @apollographql/apollo-tools to type modules config parameter.

How to reproduce the issue:

import {
  GraphQLResolverMap,
} from "apollo-graphql";
import {
  GraphQLResolverMap as GraphQLResolverMap_,
} from "@apollographql/apollo-tools";

enum SomeEnum {
  FOO = "foo",
  BAR = "bar",
}

const AnotherEnum = {
  FOO: "foo",
  BAR: "bar",
};

const resolvers1: GraphQLResolverMap<any> = {
  SomeEnum,
  AnotherEnum,
};

const resolvers2: GraphQLResolverMap_<any> = {
  SomeEnum,
  AnotherEnum,
};

Versions

  • @apollographql/apollo-tools: 0.4.1
  • apollo-graphql: 4.0.0

ooflorent avatar Nov 22 '19 10:11 ooflorent

The actual apollo server config references IResolvers.

https://github.com/apollographql/apollo-server/blob/master/packages/apollo-server-core/src/types.ts#L98

So I guess that the tools should really be using this:

https://github.com/apollographql/graphql-tools/blob/master/src/Interfaces.ts#L97

There should be no reason why the GraphQLSchemaModule goes off-piste and makes it's own type, especially as the Schema Module is essentially a composed type of two pre existing types

no1melman avatar Feb 04 '20 15:02 no1melman

Having the same problem except with calls to buildSubgraphSchema, it's parameter is a GraphQLSchemaModule which is defined in @apollo/subgraph and references it's own version of GraphQLResolverMap.

@apollo/subgraph/src/schema-helper/resolverMap.ts

import { GraphQLFieldResolver, GraphQLScalarType, DocumentNode } from 'graphql';

export interface GraphQLSchemaModule {
  typeDefs: DocumentNode;
  resolvers?: GraphQLResolverMap<any>;
}

// eslint-disable-next-line @typescript-eslint/ban-types
export interface GraphQLResolverMap<TContext = {}> {
  [typeName: string]:
    | {
        [fieldName: string]:
          | GraphQLFieldResolver<any, TContext>
          | {
              requires?: string;
              resolve: GraphQLFieldResolver<any, TContext>;
            };
      }
    | GraphQLScalarType
    | {
        [enumValue: string]: string | number;
      };
}

The other GraphQLSchemaModule is defined here

@apollographql/apollo-tools/src/buildServiceDefinition.ts

// ...
import { GraphQLResolverMap } from "./schema/resolverMap";
// ...

export interface GraphQLSchemaModule {
  typeDefs: DocumentNode;
  resolvers?: GraphQLResolverMap<any>;
}
// ...

@apollographql/apollo-tools/src/schema/resolverMap.ts

import { GraphQLFieldResolver } from "graphql";

export interface GraphQLResolverMap<TContext> {
  [typeName: string]: {
    [fieldName: string]:
      | GraphQLFieldResolver<any, TContext>
      | {
          requires?: string;
          resolve: GraphQLFieldResolver<any, TContext>;
          subscribe?: undefined;
        }
      | {
          requires?: string;
          resolve?: undefined;
          subscribe: GraphQLFieldResolver<any, TContext>;
        }
      | {
          requires?: string;
          resolve: GraphQLFieldResolver<any, TContext>;
          subscribe: GraphQLFieldResolver<any, TContext>;
        };
  };
}

Anyone have a suitable workaround? Is the Apollo team working on this?

elloboblanco avatar Jun 29 '22 22:06 elloboblanco