How to type the event of a serverless resolver sent from an Amplify GraphQL call

How to type the event of a serverless resolver sent from an Amplify GraphQL call


2

I am trying to type the event object coming from my AWS Amplify/NextJS front-end. It is sent via Amplify/GraphQL and being received in a NodeJS AWS Lambda. The typescript interface contained in import { AppSyncResolverEvent } from "aws-lambda"; – but this doesn’t quite match the event object that’s being received. Can someone please advise what I’m doing wrong?

Details:

I’m successfully firing an event from my front-end (NextJS) with aws-amplify and @aws-amplify/api-graphql packages:

import { graphqlOperation, GraphQLResult } from "@aws-amplify/api-graphql"; // "version": "2.3.11",
import { API } from "aws-amplify"; // "aws-amplify": "^4.3.28",

...
    // Retrieve schemas from Lambda
    const response = (await API.graphql<{ myAppSyncFunctionResponse: any }>(
      graphqlOperation(myAppSyncFunction),
      { input: "foo" }
    )) as GraphQLResult<{ myAppSyncFunctionResponse: string }>;

The event is being successfully received in my Serverless Lambda and resembles the following:

{
  typeName: "Mutation",
  fieldName: "myAppSyncFunction",
  arguments: { input: { ... } },
  identity: {...},
  source: ...,
  request: ...,
  prev:...
}

However, when I try to use aws-lambda in my nodejs Lambda environment:

import { AppSyncResolverEvent } from "aws-lambda";

export async function eventBridgeResolver(
  event: AppSyncResolverEvent
) {...}

AppSyncResolverEvent contains different properties:

// node_modules/@types/aws-lambda/trigger/appsync-resolver.d.ts
{
 arguments: TArguments;
    identity?: AppSyncIdentity;
    source: TSource;
    request: {
        headers: AppSyncResolverEventHeaders;
    };
    info: {
        selectionSetList: string[];
        selectionSetGraphQL: string;
        parentTypeName: string;
        fieldName: string;
        variables: { [key: string]: any };
    };
    prev: { result: { [key: string]: any } } | null;
    stash: { [key: string]: any };
}

1 Answer
1


0

I have solved this by creating my own interface based on the interface AppSyncResolverEvent in aws-lambda

import {
  type AppSyncIdentity,
  type AppSyncResolverEventHeaders,
} from 'aws-lambda'

/**
 * See https://docs.amplify.aws/cli/graphql/custom-business-logic/#lambda-function-resolver
 *
 * @param TArguments type of the arguments
 * @param TSource type of the source
 */
export interface AmplifyResolverEvent<
  TArguments,
  TSource = Record<string, any> | null,
> {
  /**
   * typeName: The name of the parent object type of the field being resolved.
   */
  typeName: string
  /**
   * fieldName: The name of the field being resolved.
   */
  fieldName: string
  /**
   * arguments: A map containing the arguments passed to the field being resolved.
   */
  arguments: TArguments
  /**
   * identity: A map containing identity information for the request. Contains a nested key 'claims' that will contains the JWT claims if they exist.
   */
  identity?: AppSyncIdentity
  /**
   * source: When resolving a nested field in a query, the source contains parent value at runtime. For example when resolving Post.comments, the source will be the Post object.
   */
  source: TSource
  /**
   * request:   The AppSync request object. Contains header information.
   */
  request: {
    headers: AppSyncResolverEventHeaders
    /** The API's custom domain if used for the request. */
    domainName: string | null
  }
  /**
   * prev: When using pipeline resolvers, this contains the object returned by the previous function. You can return the previous value for auditing use cases.
   */
  prev: { result: Record<string, any> } | null
}

I gathered this by looking at interface AppSyncResolverEvent in aws-lambda which specifically mentions Maintainer’s note: Some of these properties are shared with the Amplify resolver. It may be worth checking if changes here may be applicable there too.

And in the AppSync mapping template resolver we can see how this type is built:

Before mapping template:

## [Start] Stash resolver specific context.. **
$util.qr($ctx.stash.put("typeName", "Mutation"))
$util.qr($ctx.stash.put("fieldName", "executeSearch"))
{}
## [End] Stash resolver specific context.. **

Request Mapping Template:

## [Start] Invoke AWS Lambda data source: ExecuteSearchLambdaDataSource. **
{
  "version": "2018-05-29",
  "operation": "Invoke",
  "payload": {
      "typeName": $util.toJson($ctx.stash.get("typeName")),
      "fieldName": $util.toJson($ctx.stash.get("fieldName")),
      "arguments": $util.toJson($ctx.arguments),
      "identity": $util.toJson($ctx.identity),
      "source": $util.toJson($ctx.source),
      "request": $util.toJson($ctx.request),
      "prev": $util.toJson($ctx.prev)
  }
}
## [End] Invoke AWS Lambda data source: ExecuteSearchLambdaDataSource. **

I have posted this info to https://github.com/aws-amplify/amplify-js/issues/11113#issuecomment-1748041303 so hopefully will be incorporated soon.



Leave a Reply

Your email address will not be published. Required fields are marked *