For our web app we our currently using Firebase's free plan, and need to send emails on various events/triggers. The problem is, I thought Mailgun would work perfectly for this task along with their cloud functions but it looks like it's possible to use their API only with Firebase's paid plan which we don't intend to use right now when our app is still under development. So, we're kind of stuck without the knowledge on how to implement email functionality with Firebase with minimum amount of money (we can always pay or pay more when we are a little clear on whether our app will make any profit at all)... Can anyone help me.with any way of doing it in an (almost) free way for now - which we can upgrade any time later on?
Thanks! Piyush Soni
I am using firebase hosting and cloud functions in my personal portfolio site. I created a cloud function (http event) to send emails from the contact form section, I have not enabled the billing option but the free quote (cloud function executions) is still enough for my current needs. I'm sending emails with the following way:
Cloud function:
const functions = require('firebase-functions');
const nodemailer = require('nodemailer');
const rp = require('request-promise');
//google account credentials used to send email
const mailTransport = nodemailer.createTransport(
`smtps://[email protected]:[email protected]`);
exports.sendEmailCF = functions.https.onRequest((req, res) => {
//recaptcha validation
rp({
uri: 'https://recaptcha.google.com/recaptcha/api/siteverify',
method: 'POST',
formData: {
secret: 'your_secret_key',
response: req.body['g-recaptcha-response']
},
json: true
}).then(result => {
if (result.success) {
sendEmail('[email protected]', req.body).then(()=> {
res.status(200).send(true);
});
}
else {
res.status(500).send("Recaptcha failed.")
}
}).catch(reason => {
res.status(500).send("Recaptcha req failed.")
})
});
// Send email function
function sendEmail(email, body) {
const mailOptions = {
from: `<[email protected]>`,
to: email
};
// hmtl message constructions
mailOptions.subject = 'contact form message';
mailOptions.html = `<p><b>Name: </b>${body.rsName}</p>
<p><b>Email: </b>${body.rsEmail}</p>
<p><b>Subject: </b>${body.rsSubject}</p>
<p><b>Message: </b>${body.rsMessage}</p>`;
return mailTransport.sendMail(mailOptions);
}
Update including contact form and scripts:
<form class="rsForm" action="/sendEmailCF" method="post">
<div class="input-field">
<label>Name</label>
<input type="text" name="rsName" value="">
<span class="line"></span>
</div>
<div class="input-field">
<label>Email</label>
<input type="email" name="rsEmail" value="">
<span class="line"></span>
</div>
<div class="input-field">
<label>Subject</label>
<input type="text" name="rsSubject" value="">
<span class="line"></span>
</div>
<div class="input-field">
<label>Message</label>
<textarea rows="4" name="rsMessage"></textarea>
<span class="line"></span>
</div>
<input type="hidden" name="rsLang" value="en" />
<span class="btn-outer btn-primary-outer ripple">
<input class="formSubmitBtn btn btn-lg btn-primary" type="submit" data-recaptcha="global" value="Send">
</span>
<div id="recaptcha-global"></div>
</form>
Script to handle form submit:
$('.formSubmitBtn').on('click', function (e) {
glForm = $(this).closest('.rsForm');
var recaptchaId = 'recaptcha-' + $(this).data('recaptcha');
var rsFormErrors = false;
glFormAction = glForm.attr('action');
var rsFormFields = glForm.find('.input-field');
var rsFormName = glForm.find("[name='rsName']");
var rsFormEmail = glForm.find("[name='rsEmail']");
var rsFormMessage = glForm.find("[name='rsMessage']");
// Button ripple effect
ripple($(this).parent(), e.pageX, e.pageY);
// Reset form errors
rsFormFields.removeClass('error');
rsFormErrors = false;
// Validate form fields
if(!rsFormName.val()) {
rsFormErrors = true;
rsFormName.parent().addClass('error');
}
if(!rsFormEmail.val() || !isValidEmail(rsFormEmail.val())) {
rsFormErrors = true;
rsFormEmail.parent().addClass('error');
}
if(!rsFormMessage.val()) {
rsFormErrors = true;
rsFormMessage.parent().addClass('error');
}
if(rsFormErrors) {
// if has errors - do nothing
return false;
} else {
if(rca[recaptchaId] === undefined){
rca[recaptchaId] = grecaptcha.render(recaptchaId, {
'sitekey' : 'sitekey',
'callback' : onExecutedCaptcha,
'size' : 'invisible',
'badge':'inline'
});
} else {
grecaptcha.reset(rca[recaptchaId]);
}
grecaptcha.execute(rca[recaptchaId]);
return false;
}
});
Script to handle recaptcha response and form post:
function onExecutedCaptcha(token) {
var sendingMsg = null, textMsg = null, textErr = null;
var lang = glForm.find("[name='rsLang']").val();
if(lang == 'es') {
sendingMsg = 'Enviando mensaje...';
textMsg = 'Tu mensaje se ha enviado con \u00e9xito!';
textErr = 'Algo ha salido mal. Intenta mas tarde';
} else {
textMsg = 'Your email was sent successfully!';
textErr = 'Oops! Something went wrong. Please try again.';
sendingMsg = 'Sending email...';
}
swal({
text: sendingMsg,
button: false,
closeOnClickOutside: false,
closeOnEsc: false,
});
$.post( glFormAction,
glForm.serialize(),
function (response) {
grecaptcha.reset();
var data = jQuery.parseJSON( response );
swal.close();
if(data){
swal({
text: textMsg,
icon: "success",
});
glForm.find("input[type=text], input[type=email], textarea").val("");
} else {
swal({
text: textErr,
icon: "error",
});
}
}
);
}
If you upgrade to the Blaze plan, you will only be charged for what you use and for Cloud Functions specifically you will still get the free tier number of executions.
I don't know the specifics of your application, but while under development for most apps you are unlikely to incur more than a few dollars a month in charges (and potentially only pennies).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With