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

Support for client side resolvers (apollo-link-state) #197

Closed
tonyxiao opened this issue Nov 13, 2018 · 26 comments
Closed

Support for client side resolvers (apollo-link-state) #197

tonyxiao opened this issue Nov 13, 2018 · 26 comments
Labels
Community 👨‍👧 Something initiated by a community Enhancement 🆕 New feature or request Solved ✔️ The issue has been solved
Milestone

Comments

@tonyxiao
Copy link

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)

@MichalLytek
Copy link
Owner

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.

@MichalLytek MichalLytek added Community 👨‍👧 Something initiated by a community Discussion 💬 Brainstorm about the idea Enhancement 🆕 New feature or request labels Nov 13, 2018
@tonyxiao
Copy link
Author

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

@MichalLytek
Copy link
Owner

MichalLytek commented Nov 14, 2018

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?

@shlomokraus
Copy link

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.

@MichalLytek
Copy link
Owner

MichalLytek commented Nov 15, 2018

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 😉

@MichalLytek MichalLytek removed the Discussion 💬 Brainstorm about the idea label Nov 15, 2018
@MichalLytek MichalLytek added this to the Future release milestone Nov 15, 2018
@leops
Copy link

leops commented Dec 10, 2018

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.

@MichalLytek
Copy link
Owner

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 😛

@leops
Copy link

leops commented Dec 10, 2018

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.

@MichalLytek
Copy link
Owner

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).

@leops
Copy link

leops commented Dec 10, 2018

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.

@MichalLytek
Copy link
Owner

@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 😉

@tonyxiao
Copy link
Author

tonyxiao commented Jan 26, 2019

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

@michelcve
Copy link

@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!

@MichalLytek
Copy link
Owner

@michelcve
You can do a quick hack for making it compatible with browser:
levels3d@6ee01dc

@Jdender
Copy link

Jdender commented Feb 28, 2019

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

@MichalLytek
Copy link
Owner

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.

@MichalLytek MichalLytek added the Solved ✔️ The issue has been solved label Apr 6, 2019
@bpatram
Copy link

bpatram commented Jun 30, 2019

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).

@MichalLytek
Copy link
Owner

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.

@ahrberg
Copy link

ahrberg commented Jul 19, 2019

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);
  }

@MichalLytek
Copy link
Owner

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

@ahrberg
Copy link

ahrberg commented Jul 22, 2019

@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.

@MichalLytek
Copy link
Owner

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

@tonyxiao
Copy link
Author

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

@tonyxiao
Copy link
Author

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

@laukaichung
Copy link

@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?

@MichalLytek
Copy link
Owner

@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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Community 👨‍👧 Something initiated by a community Enhancement 🆕 New feature or request Solved ✔️ The issue has been solved
Projects
None yet
Development

No branches or pull requests

9 participants