Generating numerous POST requests through Slack message events subscriptions (Slack API Application)

I have created a unique Slack application that monitors messages sent to a specific channel. When it detects a message, it initiates an event to a URL hosted on my frontend system (Next.js), which then makes a POST request to the backend for a response. Once the response is received, it is sent back to Slack as a message.

Here is the code snippet from the route:

import { WebClient } from '@slack/web-api'

const web = new WebClient(process.env.SLACK_TOKEN)

export async function POST(req: Request) {
  const data = await req.json()
  console.log(data)

  if (data.type === 'url_verification') {
    return Response.json({ challenge: data.challenge })
  }

  if (
    !data.event.thread_ts &&
    !data.event.parent_user_id &&
    data.event.type === 'message' &&
    data.type === 'event_callback' &&
    data.event.channel === 'C06GGJVRMCK'
  ) {
    const response = await fetch(process.env.AWS_BACKEND_ENDPOINT || '', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json'
        },
        body: JSON.stringify({
          message: [
            {
              role: 'user',
              content: data.event.text
            }
          ]
        })
      }
    )

    const json = await response.json()

    await web.chat.postMessage({
      text: json.data.content,
      thread_ts: data.event.ts,
      channel: data.event.channel
    })

    return new Response(data.challenge, {
      status: 200
    })
  }

  return new Response('Ok', {
    status: 200
  })
}

However, I've noticed that multiple duplicate submissions occur whenever a single message is sent to the channel. This results in several repeated responses from the backend being sent back to Slack. Every message sent triggers approximately 3 to 4 POST requests in the backend, resulting in the delivery of 3 to 4 response messages on Slack.

I am looking for suggestions on how to prevent these multiple POST requests triggered by Slack messages and ensure that only one response is sent for each incoming message.

Any advice or alternative methods to address this issue would be greatly appreciated. Thank you for your help!

Despite trying to send an early response with status 200 'OK', I am still encountering multiple events.

Expected Outcome:

My goal was to implement a solution that effectively prevents multiple POST requests from being triggered by a single Slack message. This would ensure that only one POST request is sent to the backend and one response is sent back to Slack for each message received, eliminating redundancy and enhancing user experience.

Answer №1

After much consideration, I discovered a solution using the waitUntil() function from the RequestContext in vercel/edge. This allowed me to execute code after submitting a response and handle the fetch request from the backend, as well as send a postMessage to Slack on a separate route.

    import type { RequestContext } from "@vercel/edge";
    
    export const config = {
      runtime: "edge",
    };
    
    export default async function MyEdgeFunction(
      req: Request,
      context: RequestContext
    ) {
      const data = await req.json();
    
      if (data.type === "url_verification") {
        return new Response(JSON.stringify({ challenge: data.challenge }), {
          status: 200,
          headers: { "content-type": "application/json" },
        });
      }
    
      if (
        !data.event.thread_ts &&
        !data.event.parent_user_id &&
        data.event.type === "message" &&
        data.type === "event_callback" &&
        data.event.channel === 'C06GGJVRMCK'
      ) {
        context.waitUntil(
          fetch(`https://${process.env.VERCEL_URL}/api/slack/process-events`, {
            method: "POST",
            headers: {
              "Content-Type": "application/json",
            },
            body: JSON.stringify(data),
          })
            .then((json) => console.log({ json }))
            .catch((error) => console.log("Error fetching process-events:", error))
        );
    
        return new Response(Date.now() + "");
      }
    
      return new Response(Date.now() + "");
    }

Furthermore, in the file process-events.ts I have implemented the following:

    import { WebClient } from "@slack/web-api";
    import type { VercelRequest, VercelResponse } from "@vercel/node";
    
    const web = new WebClient(process.env.SLACK_TOKEN);
    
    export default async function handler(req: VercelRequest, res: VercelResponse) {
      const { body } = req;
      const data = body;
    
      try {
        await new Promise((resolve) => setTimeout(resolve, 1000));
        const response = await fetch(process.env.AWS_BACKEND_ENDPOINT || "", {
          method: "POST",
          headers: {
            "Content-Type": "application/json",
          },
          body: JSON.stringify({
            message: [
              {
                role: "user",
                content:
                  data.event.text ||
                  "Explain to the user that something went wrong.",
              },
            ],
          }),
        });
    
        const json = await response.json();
    
          await web.chat.postMessage({
          text: json.data.content,
          thread_ts: data.event.ts,
          channel: data.event.channel
        });
      } catch (error) {
        console.error("Error fetching Backend:", error);
      }
    }

Note: It is important to note that I only receive one submission from Slack due to the immediate 200 OK reply sent by the first route.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Exploring the integration of NextAuth's credential provider with Apollo Client

Encountered an error when using NextAuth for GraphQL authentication with Apollo client in Next.js: Hooks can only be called inside of the body of a function. import NextAuth from 'next-auth'; import Providers from 'next-auth/providers&apo ...

Using React for passing data

In the snippet found in "CameraPage.tsx", there is a logical function that is responsible for fetching camera images. This function simply makes a GET request to search for images stored in the backend, which will later be displayed on the FrontEnd. The op ...

What is the reason for the inability to utilize useState within Next JS 14?

For some time now, I have encountered difficulties when using the useState function inside a useEffect. To investigate this issue further, I decided to create a fresh project and conduct some tests. I have just set up a new project, where my only modifica ...

Typescript absolute imports are not being recognized by Visual Studio Code

Encountered a similar unresolved query in another question thread: Absolute module path resolution in TypeScript files in Visual Studio Code. Facing the same issue with "typescript": "^4.5.5". Here is the content of my tsconfig.json: { ...

When is the right time to develop a Node.js application using Typescript with dockerization

Currently, I am developing a full stack TypeScript application using Express for the server and React for the client. The folder structure of my project is organized as shown below: . ├──client/ <-- React app ├──server/ <-- Express serve ...

Encountering difficulty obtaining return value from asynchronous function, await behaving inconsistently in Vue API Service

I am in the process of developing a new service to interact with my API. I am not very familiar with async/await, but I have encountered a puzzling issue that doesn't seem to align with what I've read in the documentation. When attempting to use ...

Creating an Object Factory that preserves the type: A step-by-step guide

I developed a unique object factory to create instances of all my interfaces: interface SomeInterface { get(): string; } class Implementation implements SomeInterface { constructor() {} get() { return "Hey :D"; } } type Injectable = ...

Step-by-step guide on building an API to push form input data into a MongoDB document as a subdocument

Is it possible to achieve this using the fetch API? Which method should I use, POST or PUT? Please provide a response. Can someone please give me the complete API code to push orderSchema as a subdocument in userSchema? I have searched through numerous lin ...

Django is experiencing a CSRF verification failure because the token is not persisting between the GET and POST requests

After reviewing several older CSRF-related inquiries, I realized that the solutions provided are outdated and not applicable anymore due to changes in handling with render() or other built-in methods. So, here's my query: I have a form template rende ...

Data from HTML not being transferred by Angular Forms

I am facing an issue with transferring input data from HTML's <select> element to Angular Forms. Let's take a look at my code first. File Name: home-page.component.html <form [formGroup]="rForm" (ngSubmit)="addPaste(rForm.value)"> ...

Is there a noticeable difference in speed between a prisma.findMany() and a prisma.findUnique() query?

Do the performance levels of the two statements below show a noticeable contrast, and how do they manifest in SQL? findUnique-Query: const user = await prisma.user.findUnique({ where: { id: foo } }) findMany-Query: ...

How to eliminate empty quotes from a variable in an HTML code block using Next.js

My question is about removing HTML tags, so instead of displaying <H2>When Does Thor: Love And Thunder Release on Disney Plus?</H2>, it should show as When Does Thor: Love And Thunder Release on Disney Plus? I have clarified my question, hopin ...

Tips for implementing the handleChange event with CalendarComponent from the PrimeReact library

Hey there! I'm currently working with the CalendarComponent from the PrimeReact library in my app. I want to update the type of event being typed in the handleChange function instead of leaving it as :any. Can anyone provide some suggestions on what s ...

How can I pass GET data and make it accessible in another PHP page through ajax?

After gaining access to www.mysite.com/index.php?order_by=id asc I need to send $name AND $_GET[order_by] to autoload_process.php. How should I proceed? . . . The contents of index.php are as follows: <!DOCTYPE html> <html> <head> ...

Is it possible to create a prototype function within an interface that can group items in an array by a specific property, resulting in an array of objects containing a key and corresponding array of values?

I've been working on this code snippet and I'm trying to figure out how to make it work: Array<T>.groupBy<KeyType> (property): {key: KeyType, array: Array<T> }[]; The code looks like this: type ArrayByParameter<T, KeyType = ...

AJAX function designed to send and receive 4 specific variables using the POST

My frustration has been building for hours as I struggle to recall my AJAX! I am attempting to create a function that will send 4 variables via POST to a specified URL, using JavaScript instead of jQuery. Here is an example of what I have so far: functio ...

An issue with an unexpected character '' is encountered when uploading a local image on Next.js platform

I recently encountered an issue while trying to import a local image into my Next App using two different methods. 1. <img src={require("../image/blue-stock-trading-graph.png")} alt="hello" /> import logo from "../image/blue-stock-trading-graph ...

Is there a way to obtain the dimensions of an image link in Angular?

Looking to verify the dimensions of an image link, specifically if it is 1000px x 1000px and in jpg format. For example: https://www.w3schools.com/bootstrap/paris.jpg I have this link and need to extract the width and height of the image. Determining t ...

Combining TypeScript and ReactJS with RequireJS: A guide to importing react-dom in TypeScript

I am currently working with TypeScript and React.js. Within Visual Studio 2015, I have set the project properties to use AMD as the module system for TypeScript build, meaning I am utilizing requirejs for importing modules. Within my main.tsx file, I am r ...

Tips for incorporating asynchronous page components as a child element in next.js?

Utilizing the latest functionality in next.js for server-side rendering, I am converting my component to be async as per the documentation. Here is a simple example of my page component: export default async function Home() { const res = await fetch( ...