graphql-code-generator: [TypeScript Resolvers] Apollo Server resolvers types mismatch

I use typed resolvers generation and define my resolvers like:

import { QueryResolvers, MutationResolvers, SubscriptionResolvers, User } from './generated/graphql'

const query: QueryResolvers.Resolvers = {
  allUsers: (root, args, ctx) => {
    const result: User[] = []
    return result
  }
}

const mutation: MutationResolvers.Resolvers = {
  signup: (root, args, ctx) => {
    const res = ctx.gql.mutation.createUser({
      input: {
        user: {
          firstName: 'John Doe'
        }
      }
    })
    const result: User = { firstname: 'asdf', lastname: 'asdfasdf' }
    return result
  }
}

export default {
  mutation,
  query
}

When it comes to configure apollo-server like this:

import resolvers from './app/resolvers'

const server = new ApolloServer({
      typeDefs,
      resolvers,
      mocks: true,
      context: (c: any) => ({
        ...c,
        orm: connection,
        gql: postgraphileConnection
      })
    })

then apollo-server gives an error about types mismatch:

Type '{ mutation: Resolvers<GqlContext, {}>; query: Resolvers<GqlContext, {}>; }' is not assignable to type 'IResolvers<any, any>'.
  Property 'mutation' is incompatible with index signature.
    Type 'Resolvers<GqlContext, {}>' is not assignable to type '(() => any) | GraphQLScalarType | IEnumResolver | IResolverObject<any, any> | IResolverOptions<any, any>'.
      Type 'Resolvers<GqlContext, {}>' is not assignable to type 'IResolverObject<any, any>'.
        Index signature is missing in type 'Resolvers<GqlContext, {}>'.ts(2322)

types.d.ts(28, 5): The expected type comes from property 'resolvers' which is declared here on type 'Config & { cors?: boolean | CorsOptions; }'

What should be done in order to solve it?

About this issue

  • Original URL
  • State: closed
  • Created 5 years ago
  • Reactions: 14
  • Comments: 29 (8 by maintainers)

Commits related to this issue

Most upvoted comments

For anyone wondering where to put the useIndexSignature: true, my codegen.yml looks like this (see the last 2 lines):

overwrite: true
schema: "src/schema.graphql"
documents: null
generates:
  src/generated/graphql.ts:
    plugins:
      - "typescript"
      - "typescript-resolvers"
    config:
      useIndexSignature: true

It’s hidden behind a flag because the type safety is less strict then.

Try useIndexSignature: true

@dotansimha You are absolutely right, it’s there, and I’ve overlooked it. I am a newbie to TS and the index signature keywords were not catching my attention TBH.

I guess my larger problem was that I falsely expected it to “just work”, considering that JS + Apollo is probably the most common starting point for a new GraphQL development - which is not really a problem of the codegen, but rather of Apollo.

I may suggest dropping a code snippet to that section, so it’s just a bit more eye-catchy. Or maybe rephrase the doc like By default Apollo Server will not work with generated Resolver interface....

Thanks!

P.S. BTW, I didn’t want to compromise type safety of generated interfaces, so I ended up with this trick:

const server = new ApolloServer({ typeDefs, resolvers: resolvers as any });

Thanks @mariusmarais, this helped:

const resolvers: IResolvers = {
  Query: {
    ...Query
  },
  Mutation: {
    ...(Mutation as IResolvers)
  }
}

@dzmitry-kankalovich It’s documented in our docs website, you can even find it easily in the search: image

And there is a section for integration with Apollo Server: https://graphql-code-generator.com/docs/plugins/typescript-resolvers#integration-with-apollo-server

This is not a “known issue”, because the resolver signature is agnostic to any GraphQL platform. If you wish to have it running with Apollo Server, you’ll need to add the flag, because Apollo-Server expects a different signature than plain graphql.js.

@kamilkisiela would you mind editing your comment to remove the s? (and thanks @P4sca1 )

Hello everyone, I think I can help 😄

As @mariusmarais said Apollo resolvers are defined as string indexed dictionaries. Which is good because it allows dynamic propery access in strict mode like resolvers[varableName] and exclude otherwise possible number/symbols indexes.

There are few ways to provide string index signatures, my usual:

interface StringIndexSignatureInterface {
  [index: string]: any
}

type StringIndexed<T> = T & StringIndexSignatureInterface

// This could be recursive/deep but apollo does not seem to require it

And now you can do:

// no issues with apollo-server & no type assertion necessary
const resolvers: StringIndexed<IResolvers> = {...}

@kamilkisiela I think it’s not too weird workaround 😉

Btw. You can do something about it -there is currently open issue with ‘help wanted’ in typescript main repo to allow inferring index signatures from object literals^^

If graphql-code-generator-typescript-server is meant only for Apollo - this could be added to generated code. Otherwise - I think everything is ok (because removing index signatures is somehow harder than adding them^^) - maybe sb can mention of this ‘trick’ in docs.

//EDIT: Frankly I cannot think of use case when string index signature would become an issue, if you know one - please tell. Maybe this should be added to generate code for easier use. (@dotansimha I can PR it if you want)

For anyone else who lands here and useIndexSignature: true isn’t working — you have to kill the codegen server and restart it. The config hot reload doesn’t work for this.

I literally spent an entire day on this problem. I wish things like this would be mentioned in README, something about known issues.

@ankitjena which version do you use? It seems like your configuration is valid, the useIndexSignature: true should make it compatible with Apollo server.

@panzelva it was fixed in the recent refactor, and it will be available soon in 1.0.0.

Hello, I am not sure if relevant to this issue, but I am getting this error:

I50M6A7U6s

I am using typescript@3.3.3333 and graphql-code-generator@0.18.0

Fixed in 0.18.0 🎉

@noahseger No - Au Contraire 😏

We’re not removing typing because it’s type intersection. Prop value needs to be any & whateverwasbefore = whateverwasbefore.

Lazy Example <= turn all the strict in Options!

The only thing lost with it is allowing accessing other typed indexes and receiving proper undefined type. (which is good thing for me usually - reflecting js, allowing lookups of props - and that’s why I proposed it)

It could be tightened a bit with this:

// You can still access other (non-typed) indexes but you cannot assign anything to them
interface StringIndexSignatureInterface {
  [index: string]: never
}

type StringIndexed<T> = T & StringIndexSignatureInterface

Or there is another method using mapped type that is the strictest possible (nor access nor assign) :

// Nothings lost, nothings added except string  indexes
type AltIndexed<T, K extends string> = {
    [K in keyof T]: T[K]
} 

const altIndexedObj = obj as AltIndexed<typeof obj ,string>

It all works, check in my lazy example 🙃

I can confirm it’s the issue @mariusmarais mentioned. I don’t think we can do anything about it right now, without implementing some weird workarounds. It’s something related to TypeScript, not GraphQL Code Generator, at least for now.

As I understand it, the reason for this is because Apollo’s IResolvers is defined as a dictionary / with an index signature (see the code).

This is not the same as an object with keys, even if the keys would match the index type. TS 2.0 was supposed to fix this (I think?) but it still doesn’t work for me.

I would do the following:

import { IResolvers } from 'graphql-tools';
import resolvers from './app/resolvers'

const server = new ApolloServer({
      typeDefs,
      resolvers as IResolvers, // <------- HERE
      mocks: true,
      context: (c: any) => ({
        ...c,
        orm: connection,
        gql: postgraphileConnection
      })
    })

Although it doesn’t really fix it.

(FYI, I also have Query and Mutation, etc., and not lower-case.)