Continuing the discussion from Dgraph October Community Call:
First of all, THANK YOU for the upcoming @lambda
in 20.11 I am really excited for this!!
Not trying to be negative at all, but want to ask for a feature request extension on this knowing it isn’t even released yet.
There is so much power in directives to hold imperative data from a client perspective. GraphQL has given the example of an @upper
directive that could be used similar to how @lambda
is used to convert a string to upper case within the resolver.
Dgraph took this to the next level by really making a full javascript hook that is linked to a field by adding the lambda directive and the appropriate js function in the lambda script. This is awesome! What it is lacking though is flexibility.
If I want to create a string manipulation field one for lower case and one for upper case, I would need to write two separate predicates and assign both the lambda directive, then write two separate functions to handle each one. This would look something like:
type Item {
id: ID
title: String!
titleLC: String! @lambda
titleUP: String! @lambda
}
addGraphQLREsolvers({
"Item.titleLC": ({ parent: { title } }) => title.toLowerCase()
"Item.titleUC": ({ parent: { title } }) => title.toUpperCase()
})
While this works, it causes stress points:
- A new predicate for every type of text transformation I want.
- A new function for every predicate with the lambda directive.
What I propose is:
- Allow a client directive to pass variables
- Allow the lambda schema directive to map to a function.
Allow a client directive to pass variables
Note: This looks to be started already in the codebase as the 4th param in getBodyForLambda
Extending the schema to allow for a @variables
directive that can be applied on any field that also accepts the @lambda
directive. This variables directive would accept parameters:
@variables(func: "toUpperCase", field: "title")
@variables(func: "toLowerCase", field: "text")
In the graphql/resolver.go:894 script, forward these parameters as the 4th param when calling getBodyForLambda
Extend the GraphQLEvent
type to accept args: any | null
Then I could write [this is untested]:
type Item {
id: ID
title: String!
text: String
transform: String @lambda
}
addGraphQLResolvers({
"Item.transform": ({ parent, variables: { func = null, field = null } = { } }) => func ? parent?.[field]?.[func]?.() : null
})
Allow the lambda schema directive to map to a function.
Currently the resolver for the lambda directive is linked directly to the Type.predicate
. This makes reusing the same resolver one step more difficult:
type Item {
id: ID
title: String!
text: String
transform: String @lambda
}
type Second {
id: ID
title: String!
text: String
transform: String @lambda
}
const transform = ({ parent, variables: { func = null, field = null } = { } }) => func ? parent?.[field]?.[func]?.() : null
addGraphQLResolvers({
"Item.transform": transform,
"Second.transform": transform
})
This stress point here is needing to update the lambda script for any predicate later on that wants to use the same already created resolver.
What I propose is allowing the lambda directive to be parameterized accepting a single param which points to the resolver.
The default mapped resolver should be the field.GetObjectName() + "." + field.Name()
as is already, with a check before that to look for a param from the lambda directive.
This would allow me to use the lambda directive as the following:
addGraphQLResolvers({
transform: ({ parent, variables: { func = null, field = null } = { } }) => func ? parent?.[field]?.[func]?.() : null
})
type Item {
id: ID
title: String!
text: String
transform: String @lambda(transform)
}
type Second {
id: ID
title: String!
text: String
transform: String @lambda(transform)
}
I might have offered some pull requests but I am not familiar enough with Go nor do I understand how to read the directive from the schema or the request, sorry.