We’re sorry we missed you at re:Invent, but we can still meet!

How to use webhooks and Momento Topics to build a multi-language chat app

Embrace the diversity of global communication with Momento!

Matt Straathof
Author
Rishti Gupta
Author

Share

What are webhooks?

Webhooks serve as pivotal endpoints facilitating event-driven communication between applications. Within Momento, you can create webhooks tailored to listen to messages published on specific topics. When a message aligns with a webhook’s designated topic, the system triggers the webhook, invoking it with a comprehensive payload:

{
  cache: string;
  topic: string;
  event_timestamp: number;
  publish_timestamp: number;
  topic_sequence_number: number;
  token_id: string;
  text: string; // will look something like "{\"message\": \"this is a message\" }"
}

This payload ensures your webhook receives relevant data associated with the published message, allowing you to seamlessly integrate into the Momento ecosystem.

Building a multi-language chat app using Momento Topics & Webhooks

The constraints of English-only chat applications can be limiting. With Momento’s introduction of webhooks in conjunction with Momento Topics and Amazon Translate, users can now communicate in their preferred language, transcending language barriers for a more inclusive chat experience.

Now, the possibilities are endless – effortlessly switch between languages like Spanish, French, Hindi, and more.

Architecture

Our architecture for this application consists of a lambda rest api using aws api gateway as a proxy. We can have our lambda listen to POST requests on a route /translate, which we can then register with Momento as a webhook, to start receiving events

1. Webhook Configuration:

Set up Momento webhook to trigger a POST request to the AWS API Gateway.

2. Lambda Function:

POST /translate endpoint to listen for webhook events, validated requests come from Momento, and translate to our supported languages.

3. API Gateway:

Forwards requests through aws api gateway to our lambda function.

Getting Started

Let’s outline the steps and requirements for creating the translate API using AWS Lambda and API Gateway, leveraging the provided boilerplate repo:

Step 1: Set Up Lambda Project

Clone the boilerplate repo to initialize your Lambda project.

Step 2: Create and Deploy translate API

API Setup:

  • Create a Lambda REST API that handles requests at the /{proxy+} endpoint. To do this using AWS CDK, you can refer to an example here.
  • Lets setup the webhook route we will be using.
// Import necessary libraries and modules

// declaring globally scoped variables to prevent redeclaration on every lambda invocation
const api = createAPI({
    logger: true,
});

// Setting up CORS for our api
api.use((req: Request, res: Response, next: NextFunction) => {
    res.cors({});
    next();
});

// Required for returning CORS on errors
const errorHandler = (
    error: Error,
    req: Request,
    res: Response,
    next: NextFunction
) => {
    res.cors({});
    next();
};

// add our error handler as middleware
api.use(errorHandler);

api.post('/translate', async (req: Request, res: Response) => { 
  console.log('webhook endpoint invoked', req.body);
  return res.status(200).send('success');
});

// Our handler which get invoked every time the lambda gets called
export const handler = async (
    event: APIGatewayProxyEventV2,
    context: Context
) => {
		return api.run(event, context);
};

In this example, when our webhook /translate gets invoked, we simply log webhook endpoint invoked. We can deploy this code, register it as a webhook, and verify that its working as expected.

Step 3: Deploy the boilerplate code to get the endpoint url

Deploy the provided code to your AWS account using the following command:

npm cdk deploy

Once successfully deployed, you’ll be assigned a URL, such as This URL serves as your base_url. As you add routes to the Lambda function, they seamlessly extend from this base URL. For example, if you establish a route for /webhooks, the corresponding endpoint would be . This method simplifies API endpoint management and ensures a clear structure for accessing various functionalities within your Lambda function.

Step 4: Register the webhook with Momento

To set up a webhook with Momento, a cache is required. If you don’t have one, create it through the Momento Console here. For this guide, we’ll utilize a cache named moderator in the us-west-2 region. However, any cache in any region will seamlessly work. With the cache ready, configure a webhook within it to actively monitor the chat-publish topic. Simply navigate to the cache, select Webhooks, enter a webhook name (e.g., moderator-webhook), set the topic name as chat-publish, and designate the webhook destination as the endpoint URL obtained earlier.

Step 5: Updating handler code to validate requests are coming from Momento

After generating the webhook in Momento, a corresponding secret is generated. Navigate into the webhook to get the secret. Copy this secret and proceed to implement a JavaScript snippet for validating requests originating from Momento.

Implement the following code to perform request validation in your API. Although optional, it is recommended as a best practice to do so.

private didRequestComeFromMomento = (req: Request): boolean => {
    const hash = crypto.createHmac("SHA3-256", process.env.THE_SIGNING_SECRET);
    const hashed = hash.update(req.rawBody).digest('hex');
    return hashed === req.headers['momento-signature'];
}

Call this function on every incoming request and return a 401 status if the request fails this validation check. This step adds an extra layer of security to ensure that only valid requests from Momento are processed by your API.

Step 6: Adding code to translate and publish message to other topics

Now that the request validation logic is in place, we can proceed with the remaining implementation for the webhook API.

When publishing messages to a topic, it’s good practice to publish them as JSON objects, so their contents can be parsed, and multiple fields can be sent in a single message. We are going to be sending messages in the format.

{
  "message": string;
}

This will appear as a string in the body.text field, and we can parse it as such. Our plan is to take this message and translate it to each of our supported languages, and publish the translated message to the corresponding topic. A user can then subscribe to the chat-en or chat-es topics to listen to messages in the desired language. The code for this looks like:

const supportedLanguagesMap = {
  en: "🇺🇸 English",
  es: "🇪🇸 Español",
  fr: "🇫🇷 Français",
  ja: "🇯🇵 日本語",
  de: "🇩🇪 Deutsch",
  hi: "🇮🇳 हिन्दी",
  zh: "🇨🇳 简体中文",
  nl: "🇳🇱 Dutch",
}

api.post('/translate', async (req: Request, res: Response) => {
    // verifying the momento is sending these requests
    if (!this.didRequestComeFromMomento(req)) {
        return res.status(401).send({ message: 'unable to validate signing key' });
    }
    const parsedMessage = JSON.parse(req.body.text);
		for (const lang of Object.keys(supportedLanguagesMap)) {
        // send message to aws translate
				const translateReq = new TranslateTextCommand({
            SourceLanguageCode: 'auto',
            TargetLanguageCode: lang,
            Text: parsedMessage.message
        });
        const translateResp = await this.translateClient.send(translateReq);
        // publish message to corresponding topic
				const messageToSend = {
            timestamp: req.body.publish_timestamp,
            message: translateResp.TranslatedText ?? '',
        }
            await this.topicClient.publish('moderator-cache', `chat-${lang}`,                                         JSON.stringify(messageToSend));
    }
    // return success on completion
    return res.status(200).send({ message: 'success' });
});

Testing The Code Using the Console

To simplify the testing process, we highly recommend using Momento Console. Follow the steps below to install and test the provided backend code:

  • Navigate to our topics console.
  • Select the cache you are using and subscribe to the language . For example, you can subscribe to Japanese translations with the topic chat-ja.

• In a separate browser tab, publish to the topic chat-publish.

  • Confirm the triggering webhook by checking to see if you are receiving translated text in your first terminal window.
  • If you are not receiving translated text make sure that:

– The language you specified is a language is in the supportedLanguagesMap.

– The api endpoint you created your webhook with is correct, if not it can be updated.

– The webhook lambda that was created is being invoked by Momento.

– You are publishing/subscribing on the correct topic.

Conclusion

This blog outlines an example of how to use webhooks and Momento Topics to translate incoming messages and publish them to multiple topics in a fanout pattern. For a more comprehensive illustration of integrating a UI with this backend, along with some additional features like profanity filtering and persisting message history, check out this GitHub repository. You can explore a working example hosted on Vercel here.

Webhooks can be used for more advanced use cases, such as processing asynchronous tasks, like passing chat messages to a summarizer or saving them to a durable store. Webhooks can also be used for non-chat applications. Triggering state machines, sending slack notifications etc… the possibilities are endless 😃.

Let us know how you are using Momento Webhooks!

Share