Sendgrid emails won't send when deployed to Vercel - javascript

Recently, I decided to try out vercel for a small project and ran into issues using sendgrid with vercel. I've implemented sendgrid into nodejs/express projects before on heroku with no issues, but for some reason Vercel just doesn't work.
Even worse, sendgrid gives no errors whatsoever so its been difficult to troubleshoot. The promise just hangs forever. Everything works fine locally when testing and I've confirmed that env variables are correct and loading.
Here's my code:
const sgMail = require('#sendgrid/mail');
const { magicLinkTemplate } = require('./templates');
const sendEmail = async (msg) => {
sgMail.setApiKey(process.env.SENDGRID_API_KEY);
try {
await sgMail.send(msg)
} catch (error) {
console.error(error);
if (error.response) {
console.error(error.response.body)
}
}
}
I've tried a number of iterations, and failed every time. Has anyone else run into issues using vercel with sendgrid?

Alright, so I believe I solved my own problem.
The issue lies in how vercel handles serverless functions:
If you're on free tier, each request must resolve in 10 seconds (for pro its 60)
Vercel seems to instantly terminate a serverless function once a response is returned, aka side effects that are not finished will cease to run.
Previously, I was running the email sends as a side effect and the api response would return without waiting for sendgrid to fullfil its promise. Below is a watered down version of my original code.
router.post('/', checkUser)
async function checkUser(req, res, next) {
const { email } = req.body;
const magicLink = generateMagicLink()
sendMagicLink(email, magicLink)
res.status(200).json({})
}
The sendMagicLink function is async and would continue to run locally, but in vercel it would cease once a status was returned from the api.
New code looks like this...
router.post('/', checkUser)
async function checkUser(req, res, next) {
const { email } = req.body;
const magicLink = generateMagicLink()
await sendMagicLink(email, magicLink)
res.status(200).json({})
}
Now the serverless function stays alive because it waits for the async process to finish.
This whole thing tripped me up because if you have a persistent server, there would be no issues with the original version. Hope this helps someone!

Related

AuthRetryableFetchError: self signed certificate in certificate chain with Supabase and Next.js

I'm receiving the following error with certificates when trying to fetch the user from Supabase inside getServerSideProps with Next.js:
AuthRetryableFetchError: request to https://[redacted].supabase.co/auth/v1/user failed, reason: self signed certificate in certificate chain
at [redacted]/node_modules/#supabase/gotrue-js/dist/main/lib/fetch.js:30:16
This is a simplified version of my code for reference:
export const getServerSideProps = async ({ req, res }) => {
const supabase = createServerSupabaseClient({ req, res });
const { data: { user }, error } = await supabase.auth.getUser();
if (error) console.error(error);
return {
props: {
user,
}
}
};
I've already setup yarn and npm to both use the right certificate using yarn config set cafile /path/to/certificate/file and npm config set cafile /path/to/certificate/file respectively, but for some reason when Next.js tries to get this from the server side (Node.js) it fails, and I'm not sure what service I need to setup to tell it where the certificate is set?
There are a lot of similar questions out there, but I couldn't find any specifically about Next.js or hitting this issue in Node.js.
Any help appreciated.
So it seems to be possible to get round this issue with setting NODE_TLS_REJECT_UNAUTHORIZED=0 in env.local, but if anyone knows how to fix the issue rather than avoid it I'd be interested still.
src: https://stackoverflow.com/a/45088585/827129

Nextjs api route internal error in production

All apis are working just fine when serving locally but when i try to deploy the app in vercel/ heroku, the apis give me internal error code 500.
So i tried to make a "test api " called hello, it worked just fine until i tried to open connection with db ( query was working locally but in production it gave me internal error)
Here is my code
const mysql= require("mysql2/promise")
export default async function hello(req, res) {
const db= await mysql.createConnection({
host:"eu-cdbr-west-02.cleardb.net",
user:"########",
password:"####",
database:'####',
port:'3306',
})
const [results] = await db.execute("select * from ##",[])
db.end()
res.json(results)
}
Update: On heroku, the api is taking 10 seconds to respond back

GCP: Stripe webhook error: No signatures found matching the expected signature for payload

Stripe version: "8.107.0"
I keep getting a Stripe webhook verification error whenever I run my webhook on GCP. I've tried using the raw body in the signature, as the code snippet below mentions, as well as other ways to pass the req.rawBody as other StackOverflow answers mention.
The weird thing is that this error seems to be thrown when I deploy to GCP, and not when I run locally. I tried to manually create the signature (https://stripe.com/docs/webhooks/signatures#verify-manually), and the same result there: locally the signatures match, on GCP it doesn't.
Our server is hosted on GCP GKE, and we serve requests to our server through an Nginx Reverse Proxy. Other stack overflow solutions mentioned Google Cloud Functions and Lambda. As far as I'm aware, we do not parse requests on GCP
I do use bodyParser.json(), but that's setup after this endpoint. These are the ways I've tried creating / using a rawBody:
app.use(express.json({verify: (req,res,buf) => { req.rawBody = buf }}));
bodyParser.json({
verify: (req: any, res, buf) => {
req.rawBody = buf.toString();
},
}),
event = stripe.webhooks.constructEvent(req.rawBody, sig, webhookSecret);
I based my code on the stripe example found here: https://github.com/stripe/stripe-node/blob/master/examples/webhook-signing/node-express/express.js
// Stripe requires the raw body to construct the event
app.post('/webhook', bodyParser.raw({type: 'application/json'}), (req, res) => {
const sig = req.headers['stripe-signature'];
let event;
try {
event = stripe.webhooks.constructEvent(req.body, sig, webhookSecret);
} catch (err) {
// On error, log and return the error message
console.log(`❌ Error message: ${err.message}`);
return res.status(400).send(`Webhook Error: ${err.message}`);
}
// Successfully constructed event
console.log('✅ Success:', event.id);
// Return a response to acknowledge receipt of the event
res.json({received: true});
});
Any help would be appreciated, thanks.
The issue was with one of our setup files, where basically a space or an \n character was getting added to our webhookSecret
We had the same problem here, and it was fixed by looking for a development webhook secret (as we have a different url for development environment - it is a different webhook secret - consider look that when you have this problem).

Heroku Schedule starts running function but then exits

Problem
I have a node app deployed on Heroku and I'm trying to use Heroku Scheduler to execute tasks since node-cron doesn't work with Heroku.
The scheduler runs but it always exits with code 0. A picture of the error message is below.
I've been working on this for 4 hours already with no luck so if anyone has any suggestions that would be amazing!
Code
Here's the code in the file I'm using with Heroku Scheduler. I have it scheduled to run every 10 minutes. The getActiveComps function calls a function from another file in the node app that makes calls to an external API and then writes data to MongoDB via Mongoose.
#!/app/.heroku/node/bin/node
const getActiveComps = require('../api/functions/schedulerFunctions.js');
console.log('scheduler ran');
(async () => {
console.log('before');
await getActiveComps();
console.log('after');
})();
I never did solve the problem of running async function in a scheduled job, but I did a workaround. What I did was to make an internal API that ran the function, and make the scheduled job call the internal API to run the task. This may or may not work for you.
Defined a new API in my router:
router.get('/refresh', (req, res, next) => {
//this is the function that I needed the scheduler to run.
utils.getData().then(response => {
res.status(200).json(response);
}).catch(err => {
res.status(500).json({
error: err
});
});
})
scheduled_job.js:
const fetch = require('node-fetch');
async function run() {
try {
let res = await fetch(process.env.REFRESH_PATH);
result = await res.json();
console.log(result);
} catch (err) {
console.log(err);
}
}
run();
Of course, you should set your API path inside the heroku config vars.
Hope this helps you.

Firebase cloud function always timeout

I'm exploring the firebase cloud functions and I'm trying to send a notifications with an http request.
The problem is that even if I manage to send the notification, the request always goes timeout.
Here's my script
/functions/index.js
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
exports.friendRequestNotification = functions.https.onRequest((req, res) => {
const senderId = req.query.senderId;
const recipientId = req.query.recipientId;
const getRecipientPromise = admin.database().ref(`/players/${recipientId}`).once('value');
const getSenderPromise = admin.database().ref(`/players/${senderId}`).once('value');
return Promise.all([getRecipientPromise, getSenderPromise]).then(results => {
const recipient = results[0];
const sender = results[1];
const recipientToken = recipient.child("notificationsInfo/fcmToken").val();
const notificationAuthorization = recipient.child("notificationsInfo/wantsToReceiveNotifications").val();
const recipientBadge = recipient.child("notificationsInfo/badgeNumber").val();
const senderUsername = sender.child("username").val();
const payload = {
notification: {
title: `FriendRequest`,
body: `You have a new friend request from ${senderUsername}!`,
badge: (recipientBadge+1).toString()
}
};
if (notificationAuthorization) {
return admin.messaging().sendToDevice(recipientToken, payload).then(response => {
});
}
return admin.database().ref(`/players/${recipientId}/notificationsInfo/badgeNumber`).setValue(recipientBadge+1);
});
});
Plus It seems that the badgeNumber in never updated, is that related to the timeout issue?
HTTP-triggered Cloud Functions work just like Express apps -- you have a response object (res) that you need to use to send something when the request is done. In this case, it looks like you could do something like:
return Promise.all([
/* ... */
]).then(() => {
res.status(200).send('ok');
}).catch(err => {
console.log(err.stack);
res.status(500).send('error');
});
#Michael Bleigh answer is perfectly fine for this question, let me add more in this for the future users.
As per firebase documentation:-
Use these recommended approaches to manage the lifecycle of your
functions:
Resolve functions that perform asynchronous processing (also known as
"background functions") by returning a JavaScript promise.
Terminate HTTP functions with res.redirect(), res.send(), or res.end(). (The case in this question.)
Terminate a synchronous function with a return; statement.
Note
It's important to manage the lifecycle of a function to ensure that it resolves properly. By terminating functions correctly, you can avoid excessive charges from functions that run for too long or loop infinitely. Also, you can make sure that the Cloud Functions instance running your function does not shut down before your function successfully reaches its terminating condition or state.
You need a paid plan (Blaze, pay as you go) to access external APIs.
You might see below warning in firebase functions log if the billing account is not configured.
Billing account not configured. External network is not accessible and
quotas are severely limited. Configure billing account to remove these
restrictions
Check this link for more information.

Categories

Resources