This is the code for the NextJS API route, it is working on local environment.
export default async function handler(request, response) {
const accountSid = process.env.TWILIO_ACCOUNT_SID;
const authToken = process.env.TWILIO_AUTH_TOKEN;
const client = require('twilio')(accountSid, authToken);
await connectMongo();
const yesterday = subDays(new Date(), 1);
const pendingReminders = await Reminder.find({
reminderSent: false,
eventCanceled: false,
reminderDate: {
$gte: yesterday, // Greater than or equal to yesterday
$lte: new Date(), // Less than or equal to the current time
},
})
const currentDate = new Date();
const currentMonth = currentDate.getMonth() + 1; // Adding 1 because months are zero-based
const currentYear = currentDate.getFullYear();
console.log('pending', pendingReminders)
// TODO use Promise.all to handle all in paralell
for (let i = 0; i < pendingReminders.length; i++) {
const reminder = pendingReminders[i];
let monthlyCount = await ReminderMonthlyCount.findOne({
userId: reminder.userId,
month: currentMonth,
year: currentYear
});
if(!monthlyCount) {
monthlyCount = await ReminderMonthlyCount.create({
userId: reminder.userId,
month: currentMonth,
year: currentYear
})
}
if(monthlyCount.count < 20) {
let settings = await Settings.findOne({userId: reminder.userId})
if(!settings) {
settings = await Settings.create({userId: reminder.userId})
}
console.log(`Sending message to ${reminder.phoneNumber} from ${reminder.userId}. Scheduled for ${reminder.reminderDate}. Now it is ${new Date()}`);
client.messages
.create({
from: `whatsapp:${process.env.TWILIO_WHATSAPP_NUMBER}`,
// Whatsapp templates must be approved before on https://console.twilio.com/us1/develop/sms/senders/whatsapp-templates
// TODO Not working right now
body: replaceValues(settings?.reminderMessage, [settings?.personalOrCompanyName || 'us', format(new Date(reminder.reminderDate), "eeee 'at' h:mma"), `${process.env.NEXT_PUBLIC_WEB_URL}appointment/cancel/${reminder['_id']}`]),
to: `whatsapp:${reminder.phoneNumber}`
})
.then(async message => {
console.log(`${message.sid} message sent to ${reminder.phoneNumber} from ${reminder.userId}. Scheduled for ${reminder.reminderDate}. Now it is ${new Date()}`);
monthlyCount.count = monthlyCount.count + 1
reminder.reminderSent = true
await monthlyCount.save()
await reminder.save()
});
}
}
response.status(200).json({ok: true});
}
The problem is that when I deploy it to Vercel, it has erratic behavior. Sometimes it sends the message and sometimes it doesn't. It does not throw an error or anything. It just returns 200 ok
I think there may be a race condition between the message being sent and the API call ending before the promise resolves.
To solve this you have to switch from promises to async/await so that you know the promise and code is completed before returning from the function.
Code refactor suggestion:
export default async function handler(request, response) {
const accountSid = process.env.TWILIO_ACCOUNT_SID;
const authToken = process.env.TWILIO_AUTH_TOKEN;
const client = require('twilio')(accountSid, authToken);
await connectMongo();
const yesterday = subDays(new Date(), 1);
const pendingReminders = await Reminder.find({
reminderSent: false,
eventCanceled: false,
reminderDate: {
$gte: yesterday, // Greater than or equal to yesterday
$lte: new Date(), // Less than or equal to the current time
},
})
const currentDate = new Date();
const currentMonth = currentDate.getMonth() + 1; // Adding 1 because months are zero-based
const currentYear = currentDate.getFullYear();
console.log('pending', pendingReminders)
// TODO use Promise.all to handle all in paralell
for (let i = 0; i < pendingReminders.length; i++) {
const reminder = pendingReminders[i];
let monthlyCount = await ReminderMonthlyCount.findOne({
userId: reminder.userId,
month: currentMonth,
year: currentYear
});
if(!monthlyCount) {
monthlyCount = await ReminderMonthlyCount.create({
userId: reminder.userId,
month: currentMonth,
year: currentYear
})
}
if(monthlyCount.count < 20) {
let settings = await Settings.findOne({userId: reminder.userId})
if(!settings) {
settings = await Settings.create({userId: reminder.userId})
}
try {
console.log(`Sending message to ${reminder.phoneNumber} from ${reminder.userId}. Scheduled for ${reminder.reminderDate}. Now it is ${new Date()}`);
const message = await client.messages.create({
from: `whatsapp:${process.env.TWILIO_WHATSAPP_NUMBER}`,
// Whatsapp templates must be approved before on https://console.twilio.com/us1/develop/sms/senders/whatsapp-templates
body: replaceValues(settings?.reminderMessage, [settings?.personalOrCompanyName || 'us', format(new Date(reminder.reminderDate), "eeee 'at' h:mma"), `${process.env.NEXT_PUBLIC_WEB_URL}app/appointment/cancel/${reminder['_id']}`]),
to: `whatsapp:${reminder.phoneNumber}`
})
console.log(`${message.sid} message sent to ${reminder.phoneNumber} from ${reminder.userId}. Scheduled for ${reminder.reminderDate}. Now it is ${new Date()}`);
monthlyCount.count = monthlyCount.count + 1
reminder.reminderSent = true
await monthlyCount.save()
await reminder.save()
} catch(err) {
console.log('Twilio Client error: ', err?.message || err)
}
}
}
response.status(200).json({ok: true});
}