How can I pass an existing GraphQLSchema object like graphql-iso-date to the makeExecutableSchema function, to use it along with my string-defined types and resolver functions? Say in following type definition I want the date property to be GraphQLDate from the mentioned package.
import { GraphQLDate, GraphQLTime, GraphQLDateTime } from 'graphql-iso-date';
let typeDefs = [];
typeDefs.push(`
type MyType {
date: Date
}
`);
let resolvers = {
Query: () => { /* ... */ },
};
makeExecutableSchema({ typeDefs, resolvers });
It turns out the resolvers map, which is passed to the makeExecutableSchema does accept the GraphQLScalarType and the Date type is scalar. We still have to add the types into the typeDefs manually though...
typeDefs.push('scalar Date');
and
resolvers.Date = GraphQLDate;
So I've created a module of external scalars in my project and am doing
import externalTypes from './externalTypes';
import printType from 'graphql';
// Define my typeDefs and resolvers here
for (let externalType of externalTypes) {
let { name } = externalType;
typeDefs.push(printType(externalType));
resolvers[name] = externalType;
}
makeExecutableSchema({ typeDefs, resolvers });
I figured it by trial/failure and only then found it in the docs, thus posting. Also I still don't know how would I add a non-scalar type this way (well other than manually writing it's type definition).
Also the printType function, which prints schema definition from type object passed, becomes handy here (see this question for more detail).
Related
I am implementing a framework using Nestjs on Apollo Server using GraphQL and I would like to use some custom GraphQL scalars. I found this site, https://www.graphql-scalars.dev/docs/quick-start, which is helpful for importing custom scalars without actually implementing them as written on https://docs.nestjs.com/graphql/scalars#create-a-custom-scalar. To be specific, I would like to use BigInt, Time, and URL.
From the docs on the quick start page, I am uncertain where the code belongs at. Should I code this at app.module.ts?
// or import specific typeDefs only with ES6 Import
import { ScalarNameTypeDefinition } from 'graphql-scalars';
// or import specific typeDefs only with CommonJS
const { ScalarNameTypeDefinition } = require('graphql-scalars');
// or import all typeDefs once with ES6 Import
import { typeDefs as scalarTypeDefs } from 'graphql-scalars';
// or import all typeDefs once with CommonJS
const { typeDefs: scalarTypeDefs } = require('graphql-scalars');
const typeDefs = [
...scalarTypeDefs,
// other typeDefs
];
// or
const typeDefs = [
ScalarNameTypeDefinition,
// other typeDefs
];
my current GraphQLModule:
GraphQLModule.forRoot<ApolloDriverConfig>({
driver: ApolloDriver,
typePaths: ['./**/**/**/*.graphql'],
definitions: {
path: join(process.cwd(), 'src/graphql.ts'),
outputAs: 'class',
},
}),
How about the resolver map? Where should the code belong at? assets.resolver.ts? I also don't understand where this code belongs to?
In short, how to use graphql-scalars package in the Nestjs framework on Apollo Server? Is there any open-source GitHub repository to look into?
Have a look here NestJs Import a custom scalar
This is how my app.module.ts looks:
import { BigIntResolver, DateResolver, DateTimeResolver } from 'graphql-scalars';
GraphQLModule.forRoot<ApolloDriverConfig>({
driver: ApolloDriver,
typePaths: ['./**/*.graphql'],
definitions: {
path: join(process.cwd(), 'src/graphql/graphql-types.ts'),
customScalarTypeMapping: {
BigInt: 'bigint',
DateTime: 'Date',
},
},
resolvers: {
BigInt: BigIntResolver,
Date: DateResolver,
DateTime: DateTimeResolver,
},
playground: true,
debug: true,
}),
In my .graphql file I can then use those types:
scalar BigInt
scalar Date
scalar DateTime
input WorkperiodContent {
editedAt: DateTime
startDate: Date
endDate: Date
}
After I did this I could successfully run queries on the GraphQL Playground using those new scalars.
You don't even need to create your own custom scalar. You can just import the three that you need and you are good to go.
the enum define in OrderTypesEnum.gql
enum OrderTypes {
full_buy
pink_buy
}
import OrderTypesEnum.gql file
import OrderTypes from '#/graphql/OrderTypesEnum.gql'`
but, How to get enum in code ?
I use OrderTypes.full_buy get some error:
self.$apollo.mutate({
mutation: createOrder,
variables: {
subjectId: self.subject.id,
types: OrderTypes.full_buy
}
})
Mutation createOrderMutation error: Invariant Violation: Schema type definitions not allowed in queries. Found: "EnumTypeDefinition"
the inspect of OrderTypes type enum
Prerequisites:
< SomeEnumType > is defined in GraphQL schema (server side, no client configuration needed)
Let's assume we have:
enum SomeEnumType {
OPTION1,
OPTION2,
OPTION3
}
Apollo Client should be configured appropriate way and connected with the GraphQL API.
Then on the client side:
export const OUR_MUTATION = gql`
mutation ourMutation($foo: SomeEnumType){
ourMutation(foo: $foo){
bar
}
}
`
Only by doing this, we can pass an enum as a variable in our query or mutation. For example, with useMutation hook we can now mutate as follows:
const [ourMutation] = useMutation(OUR_MUTATION, {
variables: {
foo: "OPTION2"
},
Since the type definition in gql tag equals the definition in Schema, GraphQL recognizes a variable as an enum type despite giving it as a string.
If we want to pass an enum to variables using typescript enums we can do it as follows:
enum SomeEnumType {
OPTION1 = 0,
OPTION2 = 1,
OPTION3 = 2
}
const [ourMutation] = useMutation(OUR_MUTATION, {
variables: {
foo: SomeEnumType[SomeEnumType.OPTION1]
},
UPDATE: String enums and type generation
Personally, I recommend using string enums if possible. The usage of string enums is more straightforward.
enum SomeEnumType {
OPTION1 = "OPTION1",
OPTION2 = "OPTION2",
OPTION3 = "OPTION3"
}
...
...
variables: {
foo: SomeEnumType.OPTION1
}
For next-level coding, enum types, and all other type definitions can be automatically generated to the frontend with graphql-codegen. I really recommend using this approach, since backend schema updates and additions directly can be directly reflected in your frontend code revealing bugs and helping you code faster and more reliable.
As the error message is suggesting, Schema type definitions not allowed in queries., you can't add an enum definition in an operation document (ExecutableDefinition). You can only have operations (query, mutation, or subscription), or fragments definitions. That is, this is invalid:
enum OrderTypes {
FULL_BUY
PINK_BUY
}
mutation createOrderMutation {
...
}
If you want to define a local enum on your client, you can use the typeDefs property during ApolloClient initialization:
const client = new ApolloClient({
cache,
typeDefs: gql`
enum OrderTypes {
FULL_BUY,
PINK_BUY
}
`,
});
And then you'll be able to see the OrderTypes enum on client-side introspection (i.e Apollo extension).
Pay attention to the client-side highlight: if you try to send a request with this enum for a non-client field (i.e without the #client directive) and it makes through your server, you'll get a schema error saying that the enum type does not exist, unless you define it on your backend.
I followed the official doc to delegate a graphql schema which indicates that in order to do so, one must use the method delegateSchema that can be found on the property mergeInfo of the argument info passed down to resolvers:
resolver: (parent, args, ctx, info) => {
return info.mergeInfo.delegateSchema({
// Schema delegation options...
})
}
But there is no property mergeInfo on the info argument ! And so I get this error message: GraphQL Error GraphQL error: Cannot read property 'delegateToSchema' of undefined which is normal considering these are the top-level properties of info:
console.log(Object.keys(info))
[
'fieldName',
'fieldNodes',
'returnType',
'parentType',
'path',
'schema',
'fragments',
'rootValue',
'operation',
'variableValues',
'cacheControl'
]
It even looks like that mergeInfo is not mentioned in the type definition of the GraphQLResolveInfo object
Is the doc outdated or am I missing something ?
Thanks
mergeInfo is only injected into the info object when you use mergeSchemas to stitch together two or more schemas. Additionally, it will only be injected into the resolvers for fields you add as part of the schema stitching -- resolvers for the schemas will not be impacted (it doesn't really make sense to delegate to another schema in the context of one of the existing schemas anyway).
Here's a simple example:
const { makeExecutableSchema, mergeSchemas } = require('graphql-tools')
const schemaA = makeExecutableSchema({
typeDefs: `
type Query {
foo: String
}
`,
})
const schemaB = makeExecutableSchema({
typeDefs: `
type Query {
bar: String
}
`,
})
const typeDefs = `
extend type Query {
foobar: String
}
`
const schema = mergeSchemas({
schemas: [schemaA, schemaB, typeDefs],
})
Here, the resolvers for foo and bar will not have access to mergeInfo, but the resolver for foobar will.
I've been reading through the graphQL docs and found that they've explained the implementation of the graphql server in 2 ways: one using graphql-yoga which is a fully featured graphql server and another one is using graphql, express-graphql and express. In both cases, we pass the schema and resolver functions while creating the server instance.
But the implementation of resolver function differs. While using graphql-yoga, the resolver function is provided with 4 arguments which contains information about the parent object, arguments received, context, info. whereas in the other case (using graphql), the resolver function only gets the arguments object.
Why is that so ? If I want the info, context objects, how do I get it ?
Using graphql-yoga example: https://graphql.org/learn/execution/
Using graphql example: https://graphql.github.io/graphql-js/mutations-and-input-types/
// Code example using graphql
var express = require('express');
var graphqlHTTP = require('express-graphql');
var { buildSchema } = require('graphql');
var schema = buildSchema(`
type Query {
rollDice(numDice: Int!, numSides: Int): [Int]
}
type Mutation {
addDice(numDice: Int): String
}
`);
var root = {
rollDice({numDice, numSides}) {
return [1, 2];
},
addDice({numDice}) {
console.log("Adding something");
return "Added";
}
};
var app = express();
app.use('/graphql', graphqlHTTP({
schema: schema,
rootValue: root,
graphiql: true,
}));
app.listen(4000);
console.log('Running a GraphQL API server at localhost:4000/graphql');
// Code example using graphql-yoga
let graphqlServer = require("graphql-yoga");
const typeDefs = `
type Query {
rollDice(numDice: Int!, numSides: Int): [Int]
}
type Mutation {
addDice(numDice: Int): String
}
`;
const resolvers = {
Query: {
rollDice(parent, args, context, info) {
console.log(args.numDice);
console.log(args.numSides);
return [1, 2];
}
},
Mutation: {
addDice(parent, args, context, info) {
console.log(args.numDice);
return "Added";
}
}
};
const server = new graphqlServer.GraphQLServer({
typeDefs,
resolvers
});
server.start(() => {
console.log("server started on localhost:4000");
});
Difference between these 2 code snippets:
The resolver functions are present inside appropriate types (i.e. Query, Mutation) in one case. In the other case, they are present inside one root object. This means that I can have methods with same name in Query and Mutation in the first case, whereas in the second case that's not possible since they are keys of a single object and keys should be unique.
Why is this so ? Am I basically missing something ? How can the implementation details differ from one package to another ?
REAL TALK: the GraphQL.js docs are not that great. In my opinion, they never should have used examples with buildSchema in the first place because it understandably leads to this kind of confusion.
GraphQL.js (i.e. the graphql package) is the JavaScript implementation of GraphQL. Building a schema in GraphQL.js is done programmatically, by constructing an instance of the GraphQLSchema class:
const userType = new GraphQLObjectType({
name: 'User',
fields: {
id: {
type: GraphQLID,
},
email: {
type: GraphQLString,
},
},
});
const queryType = new GraphQLObjectType({
name: 'Query',
fields: {
user: {
type: userType,
resolve: () => ({ id: 1, email: 'john.doe#example.com' }),
},
},
});
const schema = new GraphQLSchema({
query: queryType,
})
If we print this schema in Schema Definition Language (SDL), it looks like this:
type Query {
user: User
}
type User {
id: ID
email: String
}
Working with SDL is much easier than having to write out all that code. However, GraphQL.js does not provide a way to build a fully-featured schema from SDL. It does provide a buildSchema function, but this utility constructs a schema without any resolvers (and a number of other features like union/interface type resolution).
The graphql-tools package provides a makeExecutableSchema function that lets you build a schema from SDL and a resolver map object. This is what's used under the hood by apollo-server and graphql-yoga. makeExecutableSchema constructs a schema from SDL using buildSchema and then mutates the resulting object, adding the resolvers in after the fact.
In GraphQL.js, the resolve function (or resolver) for a field takes four parameters -- the parent value, the field's arguments, the context and a GraphQLResolveInfo object. If we're creating a GraphQLObjectType like userType in the above example, this is the optional function we can provide for each of the fields in our object. This is the same function you define when you construct a resolver map to use with graphql-yoga. This is the only implementation of a field resolver.
So what's the deal with buildSchema??
The examples in the docs take advantage of GraphQL's default field resolver:
export const defaultFieldResolver: GraphQLFieldResolver<any, *> = function(
source,
args,
contextValue,
info,
) {
if (typeof source === 'object' || typeof source === 'function') {
const property = source[info.fieldName];
if (typeof property === 'function') {
return source[info.fieldName](args, contextValue, info);
}
return property;
}
};
As you can see, the default resolution logic looks for a property with the same name as the field on the source (parent) value. In our example above, the user resolver returns {id: 1, email: 'john.doe#example.com'} -- this is the value the field resolves to. The field is of the type User. We do not have a resolver defined for our id field, so the default resolver does its thing. The id field resolves to 1 because that's the value of the property named id on the parent object the resolver receives.
However, the parent value can also be a function instead of an object. If it's a function, it gets called first and then the return value is used. What does the function get called with? Well, it can't pass it a parent value (because of infinite recursion), but it can pass it the remaining three parameters (args, context and info). So that's what it does.
Now for the magic trick 🎩🐇
In our example, I can omit the resolver for the user field and pass a function to the root value instead.
const root = {
user: () => ({id: 1, email: 'john.doe#example.com'})
}
The root object is just an optional object that's passed down as the parent value to resolvers at the root level (like your Query or Mutation types). Otherwise, those resolvers would not have a parent value.
Query is an operational root type -- it serves as an "entry point" to the rest of your schema. Any fields on the Query type will be passed the root object as the parent value. If I omit a resolver for the user field, the default resolver will 1) examine the parent object for a property with the same name, 2) find a property and determine that it's a function, 3) call the function, 4) resolve the field to the return value of the function.
TADA!
However, because the function is called by the default resolver, and is not used as a resolver itself, it will only receive the three aforementioned parameters, instead of 4.
This is a neat way to work around not being able to actually provide custom resolvers for a schema, but it's very limited. It only works for root types, so we can't similarly provide fake resolvers for User fields or other types. We can't use interfaces or unions in our schema because we can't provide resolveType functions. And so on...
Hopefully that provides some clarity. And hopefully we can get the docs updated in the near future to avoid all this confusion in the first place.
Is there a way I can refactor the rootQuery object by creating more than one rootQuery objects and combine them together in the schema?
so for now we have like one rootQuery full of multiple roots and then we make a schema saying we have this one root Query. What we write is
var schema = new GraphQLSchema({
query: RootQuery
})
What I want would be like
var schema = new GraphQLSchema({
query: [RootQuery1, RootQuery2]
})
is there a way I can do this in GraphQL? it will help refactor.
No it can't, GraphQLSchema queryType only accepts a GraphQLObjectType which is RootQuery, it can't be of ArrayType with multiple GraphQLObjectType
All of the your Types should be declared in the main queryType which is RootQuery
Below is the Source code of GraphQLSchema
https://github.com/graphql/graphql-js/blob/master/src/type/schema.js#L58
export class GraphQLSchema {
_queryType: GraphQLObjectType; // accepts GraphQLObjectType not array
_mutationType: ?GraphQLObjectType;
_subscriptionType: ?GraphQLObjectType;
_directives: Array<GraphQLDirective>;
_typeMap: TypeMap;
_implementations: { [interfaceName: string]: Array<GraphQLObjectType> };
_possibleTypeMap: ?{
[abstractName: string]: { [possibleName: string]: boolean }
};