I've developed a WhatsApp webhook using AWS Lambda function in Typescript. When I test locally, everything works fine. When I deploy the application, everything works fine as soon as the deployment is finished. Hours after deployment, when you write to the webhook, it may or may not respond. You never know when the webhook will reply. Sometimes you write and it responds hours later. And when I open Cloudwatch console, I can't find the errors.
Here's my serverless.yml
service: scenario
frameworkVersion: '3'
provider:
name: aws
runtime: nodejs18.x
versionFunctions: false
stage: "dev"
region: eu-central-1
httpApi:
cors: true
deploymentBucket:
name: dba-chatbot-bucket
functions:
webhook:
handler: app/handler.Webhook
timeout: 30
events:
- httpApi:
path: /webhook
method: get
- httpApi:
path: /webhook
method: post
plugins:
- serverless-offline
- serverless-plugin-typescript
- serverless-deployment-bucket
custom:
serverless-offline:
noPrependStageInUrl: true
httpPort: 4000
Here's my handler.ts
import middy from "@middy/core";
import jsonBodyParser from "@middy/http-json-body-parser";
import { APIGatewayProxyEventV2 } from "aws-lambda";
import { container } from "tsyringe";
import "../utility";
import { CompanyChatsService } from "../service/company-chats-service";
const companyChatsService = container.resolve(CompanyChatsService);
export const Webhook = middy((event: APIGatewayProxyEventV2) => {
const httpMethod = event.requestContext.http.method.toLowerCase();
if (httpMethod === "post") {
return companyChatsService.sendMessage(event);
} else if (httpMethod === "get") {
return companyChatsService.getMessage(event);
} else {
return companyChatsService.ResponseWithError(event);
}
}).use(jsonBodyParser());
Here's my utility whatsapp-method.ts
export type WAResponseModel = {
phone_number_id: string;
phone_number: string;
name: string;
type: string;
data: any;
id: string;
};
export const getWhatsappResponse = async (body: any): Promise<WAResponseModel|boolean> => {
if (body.object) {
if (
body.entry &&
body.entry[0].changes &&
body.entry[0].changes[0] &&
body.entry[0].changes[0].value.messages &&
body.entry[0].changes[0].value.messages[0]
) {
const waResponse: WAResponseModel = {
phone_number_id: body.entry[0].changes[0].value.metadata.phone_number_id,
phone_number: body.entry[0].changes[0].value.messages[0].from,
name: body.entry[0].changes[0].value.contacts[0].profile.name,
type: body.entry[0].changes[0].value.messages[0].type,
data: body.entry[0].changes[0].value.messages[0],
id: body.entry[0].changes[0].value.messages[0].id
};
return waResponse;
}
return false;
}
return false;
};
Here's my service company-chats-service.ts, which receives get and post requests
import { APIGatewayProxyEventV2 } from "aws-lambda";
import { autoInjectable } from "tsyringe";
import { ErrorResponse } from "../utility/response";
import { getWhatsappResponse } from "../utility/whatsapp-method";
import { SendWAButtonModel, SendWAListModel, SendWATextModel, WAResponseModel } from "../models/whatsapp-message-type";
import { CredentialsRepository } from "../repository/credentials-repository";
import axios from "axios";
@autoInjectable()
export class CompanyChatsService {
constructor(
private credentialsRepository: CredentialsRepository,
async ResponseWithError(event: APIGatewayProxyEventV2) {
return ErrorResponse(404, "request method is not supported!");
}
async getMessage(event: APIGatewayProxyEventV2) {
try {
const queryParams = event.queryStringParameters;
const mode = queryParams["hub.mode"];
const verify_token = queryParams["hub.verify_token"];
const challenge = queryParams["hub.challenge"];
const credentials = await this.credentialsRepository.getByVerifyToken(verify_token)
if (mode && verify_token) {
if (mode === "subscribe" && verify_token === credentials.verify_token) {
console.log("WEBHOOK_VERIFIED");
return {
statusCode: 200,
body: challenge,
};
} else {
return {
statusCode: 403,
};
}
}
return {
statusCode: 403,
};
} catch (error) {
console.log(error);
return ErrorResponse(500, error);
}
}
async sendMessage(event: APIGatewayProxyEventV2) {
try {
const body = event.body;
let data: SendWATextModel|SendWAButtonModel|SendWAListModel;
if (await getWhatsappResponse(body)) {
const waResponse = await getWhatsappResponse(body) as WAResponseModel;
const credentials = await this.credentialsRepository.getByPhoneNumber(waResponse.phone_number_id);
const token = credentials.token;
const { status } = await axios({
method: "POST",
url:
"https://graph.facebook.com/v17.0/" +
waResponse.phone_number_id +
"/messages?access_token=" +
token,
data: {
messaging_product: "whatsapp",
to: waResponse.phone_number,
type: "text",
text: {
body: `Hi *${waResponse.name}*,\nThe chatbot is currently in development mode.\n_Thanks_`,
}
},
headers: { "Content-Type": "application/json" },
});
if (status === 200) {
return {
statusCode:200
}
} else {
return {
statusCode: 403
}
}
} else {
return {
statusCode: 403
};
}
} catch (error) {
console.log(error);
return {
statusCode: 500
};
}
}
}
I don't know what's blocking the webhook from time to time.
I've opened Cloudwatch on AWS but I can't find any errors. enter image description here