Weโre overhauling Dgraphโs docs to make them clearer and more approachable. If
you notice any issues during this transition or have suggestions, please
let us know.
To set up a lambda function, first you need to define it on your GraphQL schema
by using the @lambda directive.
For example, to define a lambda function for the rank and bio fields in
Author:
type Author {
id: ID!
name: String! @search(by: [hash, trigram])
dob: DateTime @search
reputation: Float @search
bio: String @lambda
rank: Int @lambda
isMe: Boolean @lambda
}
You can also define @lambda fields on interfaces, as follows:
interface Character {
id: ID!
name: String! @search(by: [exact])
bio: String @lambda
}
type Human implements Character {
totalCredits: Float
}
type Droid implements Character {
primaryFunction: String
}
Resolvers
After the schema is ready, you can define your JavaScript mutation function and
add it as a resolver in your JS source code. To add the resolver you can use
either the addGraphQLResolvers or addMultiParentGraphQLResolvers methods.
A Lambda Field resolver can use a combination of parents, parent, dql,
or graphql inside the function.
For example, to define JavaScript lambda functions for
Author
Character
Human
Droid
and add them as resolvers, do the following
const authorBio = ({ parent: { name, dob } }) =>
`My name is ${name} and I was born on ${dob}.`
const characterBio = ({ parent: { name } }) => `My name is ${name}.`
const humanBio = ({ parent: { name, totalCredits } }) =>
`My name is ${name}. I have ${totalCredits} credits.`
const droidBio = ({ parent: { name, primaryFunction } }) =>
`My name is ${name}. My primary function is ${primaryFunction}.`
self.addGraphQLResolvers({
"Author.bio": authorBio,
"Character.bio": characterBio,
"Human.bio": humanBio,
"Droid.bio": droidBio,
})
For example, you can add a resolver for rank using a graphql call, as
follows:
async function rank({ parents }) {
const idRepList = parents.map(function (parent) {
return { id: parent.id, rep: parent.reputation }
})
const idRepMap = {}
idRepList
.sort((a, b) => (a.rep > b.rep ? -1 : 1))
.forEach((a, i) => (idRepMap[a.id] = i + 1))
return parents.map((p) => idRepMap[p.id])
}
self.addMultiParentGraphQLResolvers({
"Author.rank": rank,
})
The following example demonstrates using the client-provided JWT to return
true if the custom claim for USER from the JWT matches the id of the
Author.
async function isMe({ parent, authHeader }) {
if (!authHeader) return false
if (!authHeader.value) return false
const headerValue = authHeader.value
if (headerValue === "") return false
const base64Url = headerValue.split(".")[1]
const base = base64Url.replace(/-/g, "+").replace(/_/g, "/")
const allClaims = JSON.parse(atob(base64))
if (!allClaims["https://my.app.io/jwt/claims"]) return false
const customClaims = allClaims["https://my.app.io/jwt/claims"]
return customClaims.USER === parent.id
}
self.addGraphQLResolvers({
"Author.isMe": isMe,
})
Example
For example, if you execute the following GraphQL query:
query {
queryAuthor {
name
bio
rank
isMe
}
}
You should see a response such as the following:
{
"queryAuthor": [
{
"name": "Ann Author",
"bio": "My name is Ann Author and I was born on 2000-01-01T00:00:00Z.",
"rank": 3,
"isMe": false
}
]
}
In the same way, if you execute the following GraphQL query on the Character
interface:
query {
queryCharacter {
name
bio
}
}
You should see a response such as the following:
{
"queryCharacter": [
{
"name": "Han",
"bio": "My name is Han."
},
{
"name": "R2-D2",
"bio": "My name is R2-D2."
}
]
}
The Human and Droid types inherit the bio lambda field from the
Character interface.
For example, if you execute a queryHuman query with a selection set containing
bio, then the lambda function registered for Human.bio is executed, as
follows:
query {
queryHuman {
name
bio
}
}
This query generates the following response:
{
"queryHuman": [
{
"name": "Han",
"bio": "My name is Han. I have 10 credits."
}
]
}