Hi! We are using Apollo Server modules to organize our code at OkCupid, and as I looked into adding custom scalars to our graph, it appears that they don't quite work with modules.
I took this example from the Apollo Server docs, added a few console logs, and updated it to pass the typeDefs and resolvers in as a module instead of on the top level.
```.js
const { ApolloServer, gql } = require('apollo-server');
const { GraphQLScalarType, Kind } = require('graphql');
const myCustomScalarType = new GraphQLScalarType({
name: 'MyCustomScalar',
description: 'Description of my custom scalar type',
serialize(value) {
console.log("serialize");
let result;
// Implement custom behavior by setting the 'result' variable
return result;
},
parseValue(value) {
console.log("parse");
let result;
// Implement custom behavior here by setting the 'result' variable
return result;
},
parseLiteral(ast) {
console.log("literal");
switch (ast.kind) {
case Kind.Int:
// return a literal value, such as 1 or 'static string'
}
}
});
const schemaString = gql`
scalar MyCustomScalar
type Foo {
aField: MyCustomScalar
}
type Query {
foo: Foo
}
`;
const resolverFunctions = {
MyCustomScalar: myCustomScalarType,
Query: {
foo: () => {
return { aField: 1 };
},
},
};
const scalarModule = {
typeDefs: schemaString,
resolvers: resolverFunctions,
};
const server = new ApolloServer({
modules: [ scalarModule ],
});
server.listen().then(({ url }) => {
console.log(🚀 Server ready at ${url})
});
```
I expect the docs to reflect the new type, and for the serialize log on line 8 to run when a request is made in playground. But while I see the the MyCustomScalar type details, the description doesn't come through. And when I execute a request, none of the myCustomScalarType functions run.
Thanks in advance for your time, and thanks for making a great tool! Please let me know if there's anything else I can provide to help debug this. I'm also quite new to GraphQL, so let me know if I've misunderstood something here!
I just ran into this now. I'm trying to create a standard Date scalar that I can use across my modules with no luck.
For what it's worth, a solution that I found is to just... not use modules. The code in the server config becomes a little more complex, but you can do the following:
```.js
import { ApolloServer, makeExecutableSchema, gql } from "apollo-server-express";
import { merge } from "lodash";
// an example module
import user from "modules/user";
const schema = makeExecutableSchema({
typeDefs: [
user.typeDefs,
// etc.
],
resolvers: merge(
user.resolvers,
// etc.
),
});
const server = new ApolloServer({
schema,
});
```
This also fixes another bug with modules — if you make a union type when using modules, Apollo doesn't tell you about (or run) the __resolveType function for them. So I'd recommend moving to the method above, even though it's more verbose.
Nice one @mgeraci... That's exactly what I ended up doing just now!
Thank you for reporting this issue!
As you've both found here, this is currently a limitation of the modules implementation in Apollo Server which internally leverages @apollographql/apollo-tools's buildServiceDefinition rather than graphql-tools's makeExecutableSchema which supports custom scalars directly in the resolver map.
Longer-term, we suspect that we'll land on a more effective module approach with buildServiceDefinition than that is currently possible with makeExecutableSchema, but the patterns you've adopted above may be better suited in for some scenarios (e.g. custom scalars) in the short-term.
This is all to say, it's on our radar, particularly as we move to introduce further modularization for federation support.
I'm not sure if this is a related issue since we aren't using modules but instead are passing {typeDefs, resolvers, formatError, context, introspection, cors, debug} directly, but after bumping the underlying dependency of graphql (graphql-js) from 0.13.2 to 14.1.1 our previously working Scalar Resolvers stopped working: parseDefault, parseLiteral, and serialize are no longer getting called.
I've tried the solution suggested by @mgeraci to explicitly call makeExecutableSchema however that doesn't address the issue.
For reference, the reason for the graphql version bump was that with the previous version, primitive scalar types weren't being properly validated (more details available here).
I've confirmed this is an issue with both apollo 2.4.6 and 2.0.4. Nothing changed on our end aside from those version bumps and I've confirmed that reverting the graphql version to 0.13.2 fixes the issue even with 2.4.6. Given that, it seems safe to conclude that the underlying issue has something changing with the interaction between apollo-server and graphql.
@abernix if you could provide any information on ETA on a fix or other potential workarounds that would be very much appreciated. Currently our options of either validate only primitive scalars or validate only custom scalars isn't workable. There also appears to be a similar issue here.
@stephenhandley That's not related to this issue. I think you've done the correct thing by opening the issue that you did above (https://github.com/graphql/graphql-js/issues/1755) on the graphql-js repository. It might be useful if you updated that issue with a minimal reproduction though, because I'm sure that I've seen parseLiteral and serialize working on [email protected] versions.
@abernix thx and sorry for the noise, as I mentioned on the linked ticket, it turns out it was the interaction between a lib of mine graphql-validated-types which was extending GraphQLScalarType and graphql where the changed implementation in 14.x.x broke my implementation
Ran into this same issue and thought I was doing __resolveType wrong... What are thoughts on using https://github.com/Urigo/graphql-modules as a nearly identical replacement?
Oh wow this caught me out big-time today! 😢 (The __resolveType function not being called when using modules)
I guess this is another thing to add to my tweet about module support and where is it going? @abernix / @trevor-scheer What is the recommendation in the meantime? Just stay away from modules?
So is this going to be fixed?
This seems crazy that it has been open for so long and is an extremely _surprising_ limiting feature
Solved.
The answer is to use modules, but not on ApolloServer (in the config), but instead to use @apollo/federated.
tl:dr;
// Change this
// const server = new ApolloServer({
// modules: [ scalarModule ],
// });
// To this
const server = new ApolloServer({
schema: buildFederatedSchema([ scalarModule ]),
});
Where buildFederatedSchema comes from @apollo/federated.
Alas, this appears to be an issue with copy-pasta code, that has evolved in only one implementation:
ResolverMap in apollo-tools (used by apollo-server in the "modules" config prop... i.e. new ApolloServer({ modules: [{ resolvers: ... }] })):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>;
};
};
}
ResolverMap in apollo-graphql (used by apollo-federation in the modules prop of buildFederatedSchema ... i.e. new ApolloServer({ schema: buildFederatedSchema([{ resolvers: ... }]) })):import { GraphQLFieldResolver, GraphQLScalarType } from "graphql";
export interface GraphQLResolverMap<TContext = {}> {
[typeName: string]:
| {
[fieldName: string]:
| GraphQLFieldResolver<any, TContext>
| {
requires?: string;
resolve: GraphQLFieldResolver<any, TContext>;
};
}
| GraphQLScalarType
| {
[enumValue: string]: string | number;
};
}
IResolvers (the root config option for resolvers when creating a new ApolloServer with the resovlers prop in the config root ... i.e. new ApolloServer({ resolvers: ... })):export interface IResolvers<TSource = any, TContext = any> {
[key: string]: (() => any) | IResolverObject<TSource, TContext> | IResolverOptions<TSource, TContext> | GraphQLScalarType | IEnumResolver;
}
Three different ways to pass resolvers, three different backing implementations. 🤷♂
Yeah that has also been a massive issue with the differing types. I think IResolvers needs to be used because it supports GraphQLScalarType
@no1melman so does the ResolverMap as exported from @apollo/federation. Indeed, it's more fully typed, too.
I used the federation packages - and it appears to work
Most helpful comment
For what it's worth, a solution that I found is to just... not use modules. The code in the server config becomes a little more complex, but you can do the following:
```.js
import { ApolloServer, makeExecutableSchema, gql } from "apollo-server-express";
import { merge } from "lodash";
// an example module
import user from "modules/user";
const schema = makeExecutableSchema({
typeDefs: [
user.typeDefs,
// etc.
],
resolvers: merge(
user.resolvers,
// etc.
),
});
const server = new ApolloServer({
schema,
});
```
This also fixes another bug with modules — if you make a union type when using modules, Apollo doesn't tell you about (or run) the
__resolveTypefunction for them. So I'd recommend moving to the method above, even though it's more verbose.