graphql-tools
graphql-tools copied to clipboard
Custom directive not working
Custom directive for input is not working in Apollo version 3
Description I'm following a tutorial at graphql-tools for Enforcing value restrictions to build a directive to validate length of string input.
input AddCycleWorkInput {
name: String! @length(max: 10)
}
Expected If the length's name input exceeds the specified, i hope throwing any ValidateError or Error
My Environment:
- OS: Windowns 10
@graphql-tools/utils: 8.1.1apollo-server-core: ^3.3.0typescript: ^4.4.3@graphql-tools/utils: 8.1.1,
Schema build
private static buildSchema(accountsGraphQL: any): GraphQLSchema {
// List directives.
const { lengthDirectiveTypeDefs, lengthDirectiveTransformer } = lengthDirective('length');
// Build schema.
let schema = makeExecutableSchema({
typeDefs: mergeTypeDefs([
lengthDirectiveTypeDefs, // This is problem
accountsGraphQL.typeDefs,
typeDefs
]),
resolvers: mergeResolvers([accountsGraphQL.resolvers, resolvers]),
schemaDirectives: {
...accountsGraphQL.schemaDirectives,
},
});
// needed to validate input fields!
schema = lengthDirectiveTransformer(schema);
return schema;
}
Constructor of Apollo Server
const server = new ApolloServer({
schema: this.buildSchema(accountsGraphQL), // Schema build
context: accountsGraphQL.context,
uploads: false,
formatError: (error) => {
if (error) {
return {
message: error.message,
status: codeToStatus[error.extensions.code.toUpperCase()] || 400,
path: error.path
};
}
}
});
Length directive, to short you can watch this link
export default function lengthDirective(directiveName: string) {
class LimitedLengthType extends GraphQLScalarType {
constructor(type: GraphQLScalarType, maxLength: number) {
super({
name: `${type.name}WithLengthAtMost${maxLength.toString()}`,
serialize(value: string) {
console.log("value1", value);
const newValue: string = type.serialize(value)
if (newValue.length > maxLength) {
throw new Error(`expected ${newValue.length.toString(10)} to be at most ${maxLength.toString(10)}`)
}
return newValue
},
parseValue(value: string) {
console.log("value2", value);
return type.parseValue(value)
},
parseLiteral(ast) {
return type.parseLiteral(ast, {})
}
})
}
}
const limitedLengthTypes: Record<string, Record<number, GraphQLScalarType>> = {}
function getLimitedLengthType(type: GraphQLScalarType, maxLength: number): GraphQLScalarType {
const limitedLengthTypesByTypeName = limitedLengthTypes[type.name]
if (!limitedLengthTypesByTypeName) {
const newType = new LimitedLengthType(type, maxLength)
limitedLengthTypes[type.name] = {}
limitedLengthTypes[type.name][maxLength] = newType
return newType
}
const limitedLengthType = limitedLengthTypesByTypeName[maxLength]
if (!limitedLengthType) {
const newType = new LimitedLengthType(type, maxLength)
limitedLengthTypesByTypeName[maxLength] = newType
return newType
}
return limitedLengthType
}
function wrapType<F extends GraphQLFieldConfig<any, any> | GraphQLInputFieldConfig>(
fieldConfig: F,
directiveArgumentMap: Record<string, any>
): void {
if (isNonNullType(fieldConfig.type) && isScalarType(fieldConfig.type.ofType)) {
fieldConfig.type = getLimitedLengthType(fieldConfig.type.ofType, directiveArgumentMap['max'])
} else if (isScalarType(fieldConfig.type)) {
fieldConfig.type = getLimitedLengthType(fieldConfig.type, directiveArgumentMap['max'])
} else {
throw new Error(`Not a scalar type: ${fieldConfig.type.toString()}`)
}
}
return {
lengthDirectiveTypeDefs: `directive @${directiveName}(max: Int) on FIELD_DEFINITION | INPUT_FIELD_DEFINITION`,
lengthDirectiveTransformer: (schema: GraphQLSchema) =>
mapSchema(schema, {
[MapperKind.FIELD]: fieldConfig => {
const lengthDirective = getDirective(schema, fieldConfig, directiveName)?.[0]
if (lengthDirective) {
wrapType(fieldConfig, lengthDirective)
return fieldConfig
}
},
})
}
}
I know this is a bug of version 3 ApolloServer, but can other way to fix it. Please help me. Thanks a lot.
I am also facing issues with custom directives when using codegen. Everything works fine without codegen, when entire schema is kept in one single file, as shown in samples here. However, when I introduced codegen to generate the schema dynamically, then the directive transformer function is not getting executed when the field is queried.
Hi!
I'm facing the same issue. Did anyone manage to solve this?
Bram
I think you may need to also add the validation logic in the parseLiteral method to handle input fields.