Intro to Lambda Resolvers for AppSync Implementation

2023-02-21

#cloud#backend
Intro to Lambda Resolvers for AppSync Implementation

This is a series of articles about setting up a complex Serverless backend infrastructure with AWS SAM and CloudFormation.

Here is the index of all the articles in case you want to jump to any of them:

1. Setup of AppSync and API Gateway with Multiple AWS Cognito User Pools

2. Configuring S3 Buckets with Permissions and Access Roles in AWS Cognito AuthRole

3. Intro to DynamoDB Resolvers for AppSync Implementation

  1. Intro to Lambda Resolvers for AppSync Implementation

5. Configuring an AWS VPC to Include Lambda Resolvers with a Fixed IP

6. Intro to Pipeline Resolvers for AppSync Implementation

7. Handling Lambda Resolver Timeouts with SNS Messages


DynamoDB resolvers are fast to set up, but they hit a wall the moment you need business logic. Validate input before writing? Aggregate data from multiple sources? Call an external API? You need compute. That's where Lambda resolvers come in.

A Lambda resolver lets you run any code you want between the GraphQL operation and the data source. AppSync invokes your function, your function does whatever it needs, and returns the result. Full control.

What Lambda Resolvers Give You

For each CRUD operation, a Lambda function handles the logic:

  • Create: Validate input, enforce business rules, integrate with other services, then store the data.
  • Read: Aggregate from multiple sources, apply complex filtering, transform the response.
  • Update: Run validation, check permissions, apply business logic before updating.
  • Delete: Archive data, notify downstream services, clean up related resources before removing.

The CloudFormation Setup

Here's how to configure a Lambda function as an AppSync data source — the function itself, its execution role, and the AppSync integration:

Resources:

  # Lambda Function
  MyLambdaFunction:
    Type: "AWS::Lambda::Function"
    Properties:
      Handler: "index.handler"
      Role: !GetAtt LambdaExecutionRole.Arn
      FunctionName: "MyAppSyncLambdaDataSource"
      Code:
        S3Bucket: "myBucket"
        S3Key: "code/myLambda.zip"
      Runtime: "nodejs14.x"
      MemorySize: 256
      Timeout: 10

  # IAM Role for Lambda Execution
  LambdaExecutionRole:
    Type: "AWS::IAM::Role"
    Properties:
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: "Allow"
            Principal:
              Service: "lambda.amazonaws.com"
            Action: "sts:AssumeRole"
      Policies:
        - PolicyName: "LambdaExecutionPolicy"
          PolicyDocument:
            Version: "2012-10-17"
            Statement:
              - Effect: "Allow"
                Action:
                  - "logs:CreateLogGroup"
                  - "logs:CreateLogStream"
                  - "logs:PutLogEvents"
                Resource: "arn:aws:logs:*:*:*"

  # AppSync Lambda DataSource
  AppSyncLambdaDataSource:
    Type: "AWS::AppSync::DataSource"
    Properties:
      ApiId: !GetAtt AppSyncAPI.ApiId
      Name: "LambdaDataSource"
      Type: "AWS_LAMBDA"
      LambdaConfig:
        LambdaFunctionArn: !GetAtt MyLambdaFunction.Arn
      ServiceRoleArn: !GetAtt AppSyncLambdaRole.Arn

  # IAM Role for AppSync to call Lambda
  AppSyncLambdaRole:
    Type: "AWS::IAM::Role"
    Properties:
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: "Allow"
            Principal:
              Service: "appsync.amazonaws.com"
            Action: "sts:AssumeRole"
      Policies:
        - PolicyName: "AppSyncLambdaPolicy"
          PolicyDocument:
            Version: "2012-10-17"
            Statement:
              - Effect: "Allow"
                Action:
                  - "lambda:InvokeFunction"
                Resource: !GetAtt MyLambdaFunction.Arn

Three resources beyond the Lambda itself: an execution role, a data source configuration, and an IAM role that lets AppSync invoke the function. That's the bridge between your GraphQL schema and your custom code.

Use DynamoDB resolvers when all you need is a pass-through. Reach for Lambda the moment you need to think before you write.

Code examples are simplified to illustrate the approach. Some adjustments may be needed for production.


Next UP: Part 5. Configuring an AWS VPC to Include Lambda Resolvers with a Fixed IP