Using Lambda on Slash GraphQL with examples - Dgraph Blog

In this blog, we will learn about lambda which gives us the ability to execute custom resolvers in JavaScript, and how easy it is to get started with it on Slash GraphQL.

We will also learn how to create a local setup to manage our lambda script and schema with the help of the Slash GraphQL CLI tool to make the developer experience much smoother.

You can find the complete source code for this example here on GitHub.

What is Lambda?

Lambda offers a way to write your custom logic in JavaScript. It integrates with your GraphQL schema for easy execution in a GraphQL API.

  • Slash GraphQL users don’t require any setup, which we will be using in this blog (To use locally with Dgraph you need to setup a Dgraph cluster with a working lambda server)
  • Declare lambda queries, mutations, and fields in your GraphQL schema as needed
  • Define lambda resolvers for them in a JavaScript file

It simplifies the job of developers, as they can build a complex backend that is rich with business logic, without setting up multiple different services. Also, you can build your backend in JavaScript, which means you can build both your frontend and backend using the same language.

Dgraph doesn’t execute your custom logic itself. It makes external HTTP requests to a user-defined lambda server. Slash GraphQL will do all of this for you, which we will be using in this blog.

There are three places where you can use the lambda directive and thus tell Dgraph where to apply custom JavaScript logic. We will use each of them in this blog.

  • Lambda fields to your types and interfaces
  • Lambda queries to the Query type
  • Lambda mutations to the Mutation type

Deploy a GraphQL backend on Slash GraphQL

You’ll need an account to create GraphQL backends on Slash GraphQL. If you don’t have an account, head over to https://slash.dgraph.io and register for your free account.

Create a GraphQL Deployment

You’ll see the empty dashboard screen when you first log into Slash GraphQL.

Just press the “Launch a backend” button. That takes you to a screen to create the backend. You can also check out the “Interactive Tutorial” if you like.

I named my deployment lambda-example, set it up in AWS US West region, and selected the Free billing plan. Clicking “Launch” spins up the backend infrastructure to serve the GraphQL app. That’ll spin for just a few moments, and once you have the green tick, it’s live.

While it’s spinning up, note down the URL of your GraphQL API. You’ll need that to connect it to the app.

That’s it — the GraphQL API for the app is up and running and you can now connect to it via the Slash GraphQL CLI by using the endpoint URL. But first, let’s create our schema.

Create Schema

To see it in action, this lambda example is built on a basic schema that stores football player details.

Let’s create a basic schema that stores football player details such as name, country, club, etc. The schema contains a single type Player, add more fields like pace, shooting, defending, etc.

Now using those fields we want to compute an overall for the player which you would have seen if you have ever played FIFA. Here comes the chance for lambda to shine, we will use it with the field overall.

Updated schema -

type Player {
  id: ID!
  name: String! @search(by: [fulltext])
  position: String! @search
  pace: Int!
  shooting: Int!
  passing: Int!
  dribbling: Int!
  defending: Int!
  physical: Int!
  overall: Int @lambda
  club: String
  country: String
}

Next up, let’s create a basic local setup.

Local setup

  • Create a folder dgraph-lambda and then store the above GraphQL schema in a file called schema.graphql.
  • Run npm init to create a package file since we want to learn how to use npm packages with lambda and npm scripts to make it easier to manage our Slash GraphQL instance.
  • Create an empty file for storing the lambda script src/index.js.
  • We will be using the Slash GraphQL CLI so let’s install that first. npm install -g slash-graphql
  • We need to provide the Slash GraphQL endpoint for performing actions on our instance, let’s store that in package.json as config object which is accessible by the npm scripts.
  "name": "dgraph-lambda",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "config": {
    "url": "YOUR-SLASH-GRAPHQL-ENDPOINT"
  },
  • Let’s add some helper npm scripts like for bundling the lambda script file (this is needed if you’re using an npm package) using webpack, submitting updated schema to Slash GraphQL. Observe how the config object is accessible within the npm scripts and see how push-lambda npm script first runs webpack with target webworker to generate the bundle dist/main.js which is then used by slash-update-lambda to submit it to Slash GraphQL.
"scripts": {
    "slash-login": "slash-graphql login",
    "push-schema": "slash-graphql update-schema -e $npm_package_config_url schema.graphql",
    "slash-update-lambda": "slash-graphql update-lambda -e $npm_package_config_url -f=dist/main.js",
    "push-lambda": "npx webpack --target=webworker && npm run slash-update-lambda",
    "slash-drop-data": "slash-graphql drop -e $npm_package_config_url -d" 
  },
  • Let’s log in and submit the schema - npm run slash-login npm run push-schema

Create lambda field resolver

Let’s create the script which will have the function for calculating the player’s overall. We need access to the fields for the current object to calculate the overall this can be achieved by using the parent object that is accessible to lambda. This function as you can see below returns the calculated overall value for the object.

Now we need a way to add it as a resolver, this can be achieved by using self.addGraphQLResolvers. Refer the docs for more info.

Added the npm package chalk to just demonstrate the process of adding an npm package to the lambda script.

npm install chalk --save

Update src/index.js with this -

const chalk = require("chalk");
async function overall({ parent }) {
  console.log(chalk.blue("Test package"));
  let paceWeight,
    shootingWeight,
    passingWeight,
    dribblingWeight,
    defenseWeight,
    physicalWeight = 0;
  if (parent.position == "Forward") {
    paceWeight = 0.3;
    shootingWeight = 0.25;
    passingWeight = 0.1;
    dribblingWeight = 0.25;
    defenseWeight = 0.03;
    physicalWeight = 0.07;
  } else if (parent.position == "Midfield") {
    paceWeight = 0.1;
    shootingWeight = 0.15;
    passingWeight = 0.35;
    dribblingWeight = 0.2;
    defenseWeight = 0.1;
    physicalWeight = 0.1;
  } else if (parent.position == "Defense") {
    paceWeight = 0.05;
    shootingWeight = 0.05;
    passingWeight = 0.1;
    dribblingWeight = 0.1;
    defenseWeight = 0.4;
    physicalWeight = 0.3;
  }
  return Math.round(
    parent.pace * paceWeight +
      parent.shooting * shootingWeight +
      parent.passing * passingWeight +
      parent.dribbling * dribblingWeight +
      parent.defending * defenseWeight +
      parent.physical * physicalWeight
  );
}
self.addGraphQLResolvers({
  "Player.overall": overall,
});

Submit lambda script to Slash GraphQL -

npm run push-lambda 

NOTE: Updating the lambda script may take up to one minute to complete

Let’s add a few players and see the above lambda in action!

Go to the API Explorer in the Slash GraphQL interface or use any GraphQL client like Postman, Insomnia, or GraphQL playground.

mutation addPlayers($players: [AddPlayerInput!]!) {
  addPlayer(input: $players) {
    player {
      name
      pace
      passing
      overall
      id
    }
  }
}

Query Variables -

{
  "players": [
    {
      "name": "Auba",
      "position": "Forward",
      "pace": 92,
      "shooting": 90,
      "passing": 85,
      "dribbling": 82,
      "defending": 80,
      "physical": 80,
      "club": "Arsenal",
      "country": "Gabon"
    },
    {
      "name": "Saka",
      "position": "Midfield",
      "pace": 92,
      "shooting": 87,
      "passing": 85,
      "dribbling": 84,
      "defending": 85,
      "physical": 82,
      "club": "Arsenal",
      "country": "England"
    },
    {
      "name": "Rowe",
      "position": "Midfield",
      "pace": 85,
      "shooting": 85,
      "passing": 90,
      "dribbling": 82,
      "defending": 78,
      "physical": 75,
      "club": "Arsenal",
      "country": "England"
    },
     {
      "name": "Willian",
      "position": "Midfield",
      "pace": 85,
      "shooting": 70,
      "passing": 68,
      "dribbling": 68,
      "defending": 67,
      "physical": 65,
      "club": "Arsenal",
      "country": "Brazil"
    }
  ]
}

Create lambda mutation resolver

We can use lambda for mutations and queries as well. At the start of the transfer window every team is looking to strengthen their squad, let’s add a Transfer type that keeps track of the transfer requests.

We would like to inform the player’s agent by contacting them about our interest in buying the player this can be achieved by using a lambda mutation resolver which sends the transfer request in form of a mail/message.

Let’s update the schema to include the Mutation type with a sendTransferRequest mutation which takes the player’s name and the current club as input then add the lambda directive.

Updated schema -

type Player {
  ...
}
type Transfer {
id: ID!
player: String!
currentclub: String!
}
type Mutation {
  sendTransferRequest(player: String!, currentclub: String!): ID! @lambda
}

Submit the updated schema-

npm run push-schema

Updated lambda script -

const chalk = require("chalk");
async function overall({ parent }) {
  ...
}
async function sendTransferRequest({ args, graphql }) {
  // Can send email/message to the player's agent by calling a service here
  const results = await graphql(
    `
      mutation($player: String!, $currentclub: String!) {
        addTransfer(input: [{ player: $player, currentclub: $currentclub }]) {
          transfer {
            id
            player
            currentclub
          }
        }
      }
    `,
    { player: args.player, currentclub: args.currentclub }
  );
  return results.data.addTransfer.transfer[0].id;
}
self.addGraphQLResolvers({
  "Player.overall": overall,
  "Mutation.sendTransferRequest": sendTransferRequest,
});

Submit the updated script -

npm run push-lambda

NOTE: Updating the lambda script may take up to one minute to complete

Time to send a transfer request for a player we want!

mutation sendTransferReq {
  sendTransferRequest(player: "Aouar", currentclub: "Lyon")
}

Create lambda query resolver

Lambda offers a way to run DQL. Let’s get the list of top forward players who have shooting higher than 80 using a lambda query that executes a DQL query.

Let’s update the schema to include the Query type with a topForwardPlayers query which returns a list of players then add the lambda directive.

For using DQL on Slash GraphQL we need to change the backend mode to Flexible from the Advanced tab of the Settings option.

Updated schema -

type Player {
  ...
}
type Transfer {
  ...
}
type Mutation {
  ...
}
type Query {
  topForwardPlayers: [Player] @lambda
}

Submit the updated schema-

npm run push-schema

Updated lambda script -

const chalk = require("chalk");
async function overall({ parent }) {
  ...
}
async function sendTransferRequest({ args, graphql }) {
  ...
}
async function topForwardPlayers({ dql }) {
  // DQL query for players with shooting higher than 80
  const results = await dql.query(`query queryPlayer {
   queryPlayer(func: type(Player), orderdesc: Player.shooting) @filter(ge(Player.shooting, 80)){
                    name: Player.name
 					position: Player.position
 					shooting: Player.shooting
 					passing: Player.passing
 					club: Player.club,
                    country: Player.country 
       }
       }`);
  return results.data.queryPlayer;
}
self.addGraphQLResolvers({
  "Player.overall": overall,
  "Mutation.sendTransferRequest": sendTransferRequest,
  "Query.topForwardPlayers": topForwardPlayers,
});

Submit the updated script -

npm run push-lambda

NOTE: Updating the lambda script may take up to one minute to complete

Let’s again ahead over to API Explorer in the Slash GraphQL interface and try the lambda query -

query topForwardPlayers{
  topForwardPlayers {
    name
    position
    shooting
    passing
  }
}

Summary

We learned how easy it is to use lambda on Slash GraphQL. Using npm packages makes it even more powerful and makes it easier to add business logic to your backend. Managing your instance with Slash GraphQL CLI is fast, too!

Check out the code used in this blog here. If you’re new to GraphQL or graph databases, Dgraph Learn has guided tutorials to walk you through the fundamentals. Signup on Slash GraphQL and give it a try, if you haven’t already!

ReferencesLambda docs: https://dgraph.io/docs/graphql/lambda/overview/ Slash GraphQL CLI docs: https://dgraph.io/docs/slash-graphql/slash-cli/overview/ DQL docs: https://dgraph.io/docs/query-language/graphql-fundamentals/ NPM scripts docs: https://docs.npmjs.com/cli/v6/using-npm/scripts

This is a companion discussion topic for the original entry at https://dgraph.io/blog/post/lambda-slash-graphql/
1 Like

Thank you for the detailed post, I was searching for something like this, However, I don’t see the images in this post. can you please fix that ?

Edi: also a question - Is it possible to do the deployments from CI (like github actions) ? both for lambdas and the schema.

My teammate helped automate all of this for our app.

Here is a snippet of the command that we use to push the lambda script also.

Thanks @amaster507 this helps a bit, but this is still very much local setup.
I am trying to figure out how to work in a team setup.

For example, for the current NodeJS app what we have is that we run lint and tests on CI (Github actions) and then deploy it to our k8s cluster directly from the Github actions. The slash-graphql cli if it could accept a token based login wherein it doesn’t have to open a browser to sign in then I think it should work.
A similar example is doctl (the digital ocean cli) which allows signing in with private API keys.

the ideal setup for me would be that when a github PR is approved, the actions check for if there was any change in the schema and then subsequently deploys it.

Just use the -t flag:

USAGE
  $ slash-graphql update-lambda

OPTIONS
  -e, --endpoint=endpoint  Slash GraphQL Endpoint
  -f, --file=file          (required) Lambda script file path.
  -q, --quiet              Quiet Output
  -t, --token=token        Slash GraphQL Backend API Tokens

EXAMPLES
  $ slash-graphql update-lambda -e https://frozen-mango.cloud.dgraph.io/graphql -f <filepath>
  $ slash-graphql update-lambda -e 0x1234 -f /home/user/Downloads/script.js
1 Like