You are viewing the preview version of this book
Click here for the full version.

Lambda

(Official docs)

The Lambda data source calls the configured Lambda function and returns its result. This is the most versatile data source, as a Lambda function can do any processing, such as getting items from databases, call other functions, and even interact with third-party resources. Because of this, it's a best practice to implement complex functionality with a Lambda data source.

Configuring the data source

To add the data source, you'll need two things: the Lambda function that AppSync will call and a role that gives permission to do that. As usual, the role needs to allow the AppSync service in its trust policy:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Service": "appsync.amazonaws.com"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}

Then it needs to allow the lambda:InvokeFunction in its permissions policy:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "",
      "Effect": "Allow",
      "Action": [
        "lambda:InvokeFunction",
      ],
      "Resource": [
        "arn:aws:lambda:...:function:..."
      ]
    },
  ]
}

Notice that there are two layers of permissions working here. First, AppSync uses a role to call the Lambda function. But then the Lambda function uses another role to access resources in the account, such as databases or other functions.

Lambda data source permissions
Note

Use environment variables for the Lambda function to pass configuration to the function, such as the name of DynamoDB tables, S3 bucket names, and other non-changing values.

Event object

In the request mapping template you can define what the Lambda function gets in the event object. For example, this template passes the field arguments and an constant text to the function:

{
  "version": "2018-05-29",
  "operation": "Invoke",
  "payload": {
    "arguments": $util.toJson($ctx.arguments),
    "extra_data": "something else"
  }
}

For a field, such as field(fieldArg: String), the event object will be:

{
  "arguments":{
    "fieldArg":"arg1"
  },
  "extra_data":"something else"
}

This gives a flexible structure: the Lambda function can do a specific thing and the AppSync resolver calls it with the appropriate arguments. For example, you might need to create Cognito users in various parts in the API. For this, you can write one function that gets the email address and other required parameters and create the user. Then the mapping templates can adapt the GraphQL arguments to the Lambda. This is especially useful for mutations.

Batching

The Lambda function runs for every value that can result in several calls for a single query. This usually happens when one of the upper fields in the query returns a list.

For example, when the users are managed by Cognito, the email field requires a call to the service. When this query runs:

query project {
  users {
    email
  }
}

AppSync first resolves the project field, then moves to the users. This returns a list, so the email resolver runs for every single user in the result list. If a project has 100 users, that means there are 100 Lambda calls.

Batching is a performance boost in cases like this one: instead of calling the function for every single item, AppSync groups the requests into batches and sends a list.

See more in the official docs.

Direct Lambda resolver

(Official docs)

Lambda is a special data source as the request and the response mapping templates are optional. If the request template is missing, the whole context object is passed to the Lambda function as the event.

To extract the various parts, it's best practice to destructure the incoming object:

exports.handler = async (event, context) => {
  const {arguments, prev, stash, identity, source, info} = event;

  // ...
};

The various fields:

  • arguments: the arguments passed to the field
  • prev: in case of a pipeline resolver, this is the result of the previous step
  • stash: in a pipeline resolvers, this is the stash
  • identity: contains information about the caller user
  • source: the parent object (if any)
  • info: information about the GraphQL query for this field

For example, a unit (non-pipeline) resolver might get these values:

There is more, but you've reached the end of this preview
Read this and all other chapters in full and get lifetime access to:
  • all future updates
  • full web-based access
  • PDF and Epub versions