Skip to content

AWS graphql subscription returning null when mutation is made inside lambda

0

I am making an appsync mutation from a lambda function to update a dynamodb table. The issue is that the subscription from the frontend returns null even though the mutation is successful. Please see the code below:

Custom appsync client

import crypto from "@aws-crypto/sha256-js";
import { defaultProvider } from "@aws-sdk/credential-provider-node";
import { SignatureV4 } from "@aws-sdk/signature-v4";
import { HttpRequest } from "@aws-sdk/protocol-http";
import { default as fetch, Request } from "node-fetch";

interface AppSyncProps {
  endpoint: string; // AppSync GraphQL endpoint URL
  region: string; // AWS region of the API
}

export class AppSyncConnector {
  private endpoint: string;
  private region: string;

  constructor(props: AppSyncProps) {
    this.endpoint = props.endpoint;
    this.region = props.region;
  }

  async mutate<TInput>(query: string, variables: TInput) {
    console.log(this.endpoint);
    console.log(this.region);
    const endpoint = new URL(this.endpoint);
    const signer = new SignatureV4({
      credentials: defaultProvider(),
      region: this.region,
      service: "appsync",
      sha256: crypto.Sha256,
    });

    const requestToBeSigned = new HttpRequest({
      method: "POST",
      headers: {
        "Content-Type": "application/json",
        host: endpoint.host,
      },
      hostname: endpoint.host,
      body: JSON.stringify({ query, variables }),
      path: endpoint.pathname,
    });

    const signed = await signer.sign(requestToBeSigned);
    const request = new Request(this.endpoint, signed);

    let statusCode = 200;
    let body: Record<string, any> = {};
    let response;

    try {
      response = await fetch(request);

      if (!response.ok) {
        throw new Error(`HTTP ${response.status}: ${response.statusText}`);
      }

      body = (await response.json()) as Record<string, any>;
      if (body.errors) statusCode = 400;
    } catch (error) {
      statusCode = 400;
      body = {
        errors: [
          {
            status: response?.status,
            message: (error as Error).message,
            stack: (error as Error).stack,
          },
        ],
      };
    }

    return {
      statusCode,
      body: JSON.stringify(body),
    };
  }
}

Notifications Table Model

import { a } from "@aws-amplify/backend";

export const Notifications = a
  .model({
    notificationId: a.string().required(),
    recipientId: a.string().required(),
    notificationType: a.enum(["LIKE", "FOLLOW"]),
    isSeen: a.boolean().required(),
    actorId: a.string().required(),
    actorHandle: a.string().required(),
    actorName: a.string().required(),
    resourceId: a.string(),
  })
  .identifier(["notificationId"])
  .authorization((allow) => [allow.authenticated().to(["read", "update"])]);

Frontend

function RouteComponent() {
  const data = useRouteContext({ from: "/(authenticated)/_layout" });
  useEffect(() => {
    const createNotifSub = client.models.Notifications.onCreate().subscribe({
      next: (a) => console.log(a),
      error: (error) => console.log(error),
    });
    return () => createNotifSub.unsubscribe();
  }, []);

2 Answers
1
Accepted Answer

As I do not see your mutation object, I am not sure, but you must make sure that your mutation selects all the fields that may be needed in a subscription. This looks a little weird, that even though the mutation itself does not need any fields to be returned, if you do not do that, the subscriptions will also not return anything.

AWS
EXPERT

answered a year ago

  • Thank you! My graphql mutations were not updated using codegen and were using the old schema. As a result, some fields were missing when making mutations

0

The issue with your AppSync mutation returning null in the subscription is likely due to a missing resolver for your mutation. When you make a mutation from your Lambda function, AppSync needs a properly configured resolver to process the request and return data.

Here are the steps to fix this issue:

  1. Check your mutation resolver: Make sure you have a resolver attached to your mutation in the AppSync console. Navigate to the Schema section and look for your mutation in the Resolvers list.

  2. Configure the resolver properly: If you don't have a resolver or it's not properly configured, attach one. For a simple case, you can use a VTL resolver with a NoneDataSource:

{
"version": "2017-02-28",
"payload": {
"notificationId": "$context.arguments.notificationId",
"recipientId": "$context.arguments.recipientId",
"notificationType": "$context.arguments.notificationType",
"isSeen": "$context.arguments.isSeen",
"actorId": "$context.arguments.actorId",
"actorHandle": "$context.arguments.actorHandle",
"actorName": "$context.arguments.actorName",
"resourceId": "$context.arguments.resourceId"
}
}
  1. Ensure your mutation returns data: Your Lambda function should be returning the created/updated notification object. The subscription will only receive what the mutation returns.

  2. Check subscription setup: Ensure your subscription is properly set up with the @aws_subscribe directive pointing to the correct mutation.

  3. Network handling: If you're testing on a mobile device, be aware that network changes (like switching from WiFi to mobile data) can disrupt AppSync subscriptions. The AWS Amplify SDK has built-in reconnection capabilities, but you might need to handle connection state changes.

By ensuring your mutation resolver is properly configured to return the notification data, your subscription should start receiving the expected data instead of null.
Sources
Why do Appsync Mutations return null | AWS re:Post
Example for AppSync subscription resolver to work | AWS re:Post
AWS AppSync Subscriptions Disrupt on Network Changes | AWS re:Post

answered a year ago

You are not logged in. Log in to post an answer.

A good answer clearly answers the question and provides constructive feedback and encourages professional growth in the question asker.