Set up a newsletter signup form with Google reCAPTCHA, save emails to a Brevo
Create a simple newsletter signup using Node.js, Brevo (formerly Sendinblue), and Google reCAPTCHA.
npm install -S sib-api-v3-sdk express body-parser node-fetch@^2.6.6
brevo.json
:{ "key": "YOUR_BREVO_API_KEY" }
Optional โ Create the credential file from a block:
const credentials = loadBlock('brevo');
_fs.writeFileSync(`${__dirname}/brevo.json`, credentials);
localhost
worker1.znote.io
Create a simple HTML form using Bootstrap and reCAPTCHA v3:
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha3/dist/css/bootstrap.min.css" rel="stylesheet">
<script src="https://www.google.com/recaptcha/api.js?render=YOUR_PUBLIC_RECAPTCHA_KEY"></script>
<form id="subscription-form">
<div id="form-feedback" class="alert alert-success d-none">Thank you! Weโll be in touch.</div>
<input type="email" class="form-control mb-2" id="email" placeholder="Email" required>
<button id="submit-form" class="btn btn-primary w-100">Subscribe</button>
</form>
<script>
document.getElementById("subscription-form").addEventListener("submit", function(event) {
event.preventDefault();
const email = document.getElementById("email").value;
if (!email.includes("@")) return;
grecaptcha.ready(() => {
grecaptcha.execute('YOUR_PUBLIC_RECAPTCHA_KEY', { action: 'submit' }).then((token) => {
document.getElementById("form-feedback").classList.remove("d-none");
document.getElementById("submit-form").disabled = true;
const data = new URLSearchParams();
data.append('email', email);
data.append('g-recaptcha-response', token);
fetch("http://localhost:4000/subscribe", {
method: "POST",
body: data
});
});
});
});
</script>
//exec: node
//hide
const express = require('express');
const bodyParser = require('body-parser');
const fetch = require('node-fetch');
const SibApiV3Sdk = require('sib-api-v3-sdk');
const credentials = require('./brevo.json');
const app = express();
app.use(bodyParser.urlencoded({ extended: true }));
// Setup Brevo client
const defaultClient = SibApiV3Sdk.ApiClient.instance;
defaultClient.authentications['api-key'].apiKey = credentials.key;
const apiInstance = new SibApiV3Sdk.ContactsApi();
app.get('/', (req, res) => res.send('OK'));
app.post('/subscribe', async (req, res) => {
try {
const recaptchaRes = await fetch("https://www.google.com/recaptcha/api/siteverify", {
method: "POST",
body: new URLSearchParams({
secret: 'YOUR_PRIVATE_RECAPTCHA_KEY',
response: req.body["g-recaptcha-response"]
})
});
const captchaValidation = await recaptchaRes.json();
if (!captchaValidation.success) return res.status(403).send("Captcha failed");
const email = req.body.email;
const contactData = {
email: email,
listIds: [2], // Replace with your list ID from Brevo
updateEnabled: false
};
await apiInstance.createContact(contactData);
return res.send("done");
} catch (error) {
console.error("Subscription error:", error);
return res.status(500).send("error");
}
});
app.listen(4000, () => console.log("Server running on http://localhost:4000"));
This template lets you: