Type-graphql: Support for client side resolvers (apollo-link-state)

Created on 13 Nov 2018  路  26Comments  路  Source: MichalLytek/type-graphql

Does type-graphql support client side resolvers for use with apollo-link-state? (https://blog.apollographql.com/the-future-of-state-management-dd410864cae2, https://hackernoon.com/storing-local-state-in-react-with-apollo-link-state-738f6ca45569)

Community Enhancement Solved

Most helpful comment

I was just looking into this same issue today, so I am totally +1 on this. Whoever writes his backend with type-graphql will want the same syntax for the client resolvers.

All 26 comments

I haven't found it useful as apollo-link-state has no typedef, so declaring graphql types makes no sense here.

Although, some of the frameworkish tools like middlewares or guards might help in creating resolvers for managing state, so it might help in an other way.

But no, for now it won't work as TypeGraphQL build the executable schema using graphql-js, not a resolvers map object. Maybe there are some tools that can convert it to the proper format, I don't know.

Actually it does have typdefs for apollo client devtools. And it can still help with generating the resolvers from a set of classes.

Actually it does have typdefs for apollo client devtools.

I haven't found any info about it even in issues. The official docs says that there's no type checking:
https://www.apollographql.com/docs/link/links/state.html#type-checking
Could you give me some link or example describing how it works in apollo client devtools?

I was just looking into this same issue today, so I am totally +1 on this. Whoever writes his backend with type-graphql will want the same syntax for the client resolvers.

But no, for now it won't work as TypeGraphQL build the executable schema using graphql-js, not a resolvers map object. Maybe there are some tools that can convert it to the proper format, I don't know.

Actually, it wasn't as hard as I thought:

export interface EnumValuesMap {
  [key: string]: any;
}

export interface TypeFieldsMap {
  [fieldName: string]: GraphQLFieldResolver<any, any>;
}

export interface ResolversMap {
  [typeName: string]: TypeFieldsMap | GraphQLScalarType | EnumValuesMap;
}

export async function buildResolversMap(options: BuildSchemaOptions): Promise<ResolversMap> {
  const schema = await buildSchema(options);
  const typeMap = schema.getTypeMap();
  return Object.keys(typeMap)
    .filter(typeName => !typeName.includes("__"))
    .reduce<ResolversMap>((resolversMap, typeName) => {
      const type = typeMap[typeName];
      if ("getFields" in type) {
        const fields = type.getFields();
        resolversMap[typeName] = Object.keys(fields).reduce<TypeFieldsMap>(
          (fieldsMap, fieldName) => {
            const field = fields[fieldName];
            if ("resolve" in field && field.resolve) {
              fieldsMap[fieldName] = field.resolve;
            }
            return fieldsMap;
          },
          {},
        );
      }
      if (type instanceof GraphQLScalarType) {
        resolversMap[typeName] = type;
      }
      if (type instanceof GraphQLEnumType) {
        resolversMap[typeName] = type.getValues().reduce<EnumValuesMap>((enumMap, enumValue) => {
          enumMap[enumValue.name] = enumValue.value;
          return enumMap;
        }, {});
      }
      return resolversMap;
    }, {});
}

But for sure it's not a good idea to add a whole graphql-js package just for extracting resolvers map.

Feel free to experiment with this workaround, maybe I will take care about the proper integration someday 馃槈

I just wanted to add that I've been successful in using type-graphql in a client-side application with apollo-link-schema (not apollo-link-state). Specifically, my use case is using React Native instead of a browser, but I still needed to craft a "full" browser build of the library to remove the dependency on the glob library and make the isClass checks compatible with classes compiled by Babel. My fork is available here, I initially didn't open a pull request since I thought this to be a very specific use case not worth the possible maintenance cost (also my patch for the gulp config is a terrible hack), but it might actually be interesting if there is some traction for using the full type-graphql library in a client.

Creating separate builds will be possible in the future, while switching to monorepo with multiple plugin packages. So then I can publish @typegraphql/browser module without the glob and other things.

And using it in browser in apollo-link-schema is not a good idea as it's a huge chunk of js. But in case of RN it makes no difference, so it makes sense to create a build for it.

make the isClass checks compatible with classes compiled by Babel.

Why do you still have to transpile ES6 classes? I thought that RN has a bit modern babel setup than ES5 馃槢

Unfortunately in order to support iOS React Native has to run on a somewhat older version of JavaScriptCore, so we still have to transpile ES6.
I didn't remember the details correctly though, the error isn't directly related to transpiling classes but to transpiling arrow functions (prototype = undefined) to regular functions (prototype = Function), so checking for the existence of a prototype to detect whether the argument of a decorator is a function of a class doesn't work.

Right, that's the point of the check. But does babel transpile also the Function.prototype.name that is used to get the name of the class by TypeGraphQL? Maybe it's safer to check for that (detect class vs anonymous function).

A class is transpiled to a named function so the name is correctly set. In some case like const func = () => {} babel will transpile the arrow function to a named function called func (so that eg. React can use the name of the function in the devtools) but in the context of an inline decorator call this shouldn't matter, and the name of the function will properly be set to an empty string.

@tonyxiao @shlomokraus
Guys, please check the new 0.17 beta version with support for typeDefs and resolvers:

npm i type-graphql@next
https://19majkel94.github.io/type-graphql/docs/next/bootstrap.html#create-typedefs-and-resolvers-map

And let me know if it really works with apollo-link-state 馃槈

Whoa this is super exciting. I'll give it a spin as soon as I can.

@19majkel94 I gave it a quick spin, but personally I guess I'll wait until you've moved to the mono repo. The glob dependency (which in turn depends on fs) would force me to use something like browserify, which I don't want.

Nevertheless, a great move, and I'm looking forward to using it clientside as soon as the dependencies are sorted out ;-) Appreciate the hard work you're putting in this library!

@michelcve
You can do a quick hack for making it compatible with browser:
https://github.com/levels3d/type-graphql/commit/6ee01dc74b0b80dc98363ff4daafe90d1f009d9a

I just want to point out that Apollo Client 2.5 has client side state built in, it no longer requires apollo-link-state.

I've just added an Apollo client state example:
https://github.com/19majkel94/type-graphql/tree/master/examples/apollo-client

I've also tune a little bit some things to make it works correctly, so next release will be fully compatible with Apollo client state 馃帀 So I can finally close this issue as done 馃敀

In the future there will be @typegraphql/client package which will fix the need to provide aliases in bundler for server/node packages.

hi @19majkel94, hate to comment on a closed issue but I couldn't find an issue or card somewhere that is tracking the progress on having a @typegraphql/client export. Is this still in the plans for future release?

I'm very excited to use this in my projects but the package aliasing requires a bit more custom configuration that isn't possible for simple projects using create-react-app (not impossible of course, I can always eject or use craco but would prefer not to).

Is this still in the plans for future release?

Of course. Maybe I should write down my plans on monorepo and multiple packages + plugin system.

I'm having an issue with the @Arg decorator and not the other decorators using create react app. Sorry for not knowing the details behind but should it be possible to use like this?

Module parse failed: Unexpected character '@' (67:37)
You may need an appropriate loader to handle this file type.
|   _createClass(MyClass, [{
|     key: "myResolver",
>     value: function myResolver(@Arg("id")
|     id, _ref) {
|       var cache = _ref.cache;
  @Mutation(returns => Boolean, { nullable: true })
  public myResolver(
    @Arg("id") id: string,
    @Ctx() { cache }: IApolloContext
  ) {
    this.makeChange(cache, id);
  }

@ahrberg Possibly related to #55. The babel plugin babel-plugin-transform-typescript-metadata handles param decorators.

@19majkel94 thanks for reply. Maybe it's related. Create React App seams to use @babel/plugin-proposal-decorators. Maybe it have problems with methods parameters as you found. I think I will move on since Apollo Cache is quite verbose for client state handling.

plugin-proposal-decorators doesn't support param decorators, it's patched by babel-plugin-transform-typescript-metadata.

Finally got a chance to try this today. However getting the following problem
image

it would be a lot easier of graphql can be omitted when used client side

@typegraphql/client

Is it possible to use GraphQL Code Generator to generate the typings for the client side resolvers at https://github.com/MichalLytek/type-graphql/tree/master/examples/apollo-client?

@laukaichung you would need to emit schema file base on the client side schema as a script/build step separately, then you can point/merge server and client schema together with queries to generate the typings.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

limenutt picture limenutt  路  3Comments

winuxue picture winuxue  路  4Comments

MichalLytek picture MichalLytek  路  3Comments

laukaichung picture laukaichung  路  3Comments

maplesteve picture maplesteve  路  3Comments