Lambda Fields
Schema
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:
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
Once 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.
parents
, parent
, dql
, or graphql
inside the function.
parent
for the resolver function. You
can find additional resolver examples using dql
in the Lambda queries
article, and using graphql
in the Lambda
mutations article.
For example, to define JavaScript lambda functions for…
Author
,Character
,Human
, andDroid
…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,
});
An example, adding a resolver for rank
using a graphql
call:
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,
});
An example of using the client provided JWT to return true if the custom claim
for USER
from the JWT matches the Author’s id.
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 base64 = 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
If you execute this lambda query
query {
queryAuthor {
name
bio
rank
isMe
}
}
You should see a response such as
{
"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 this lambda query on the Character
interface
query {
queryCharacter {
name
bio
}
}
You should see a response such as
{
"queryCharacter": [
{
"name": "Han",
"bio": "My name is Han."
},
{
"name": "R2-D2",
"bio": "My name is R2-D2."
}
]
}
Note that the Human
and Droid
types will 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
will be executed:
query {
queryHuman {
name
bio
}
}
Response:
{
"queryHuman": [
{
"name": "Han",
"bio": "My name is Han. I have 10 credits."
}
]
}