Implementing Webhooks in NodeJS!!
Ever heard someone yapping about something called webhooks, and you just go blank? Okay, trust me, this will not be the case after this blog.
So what are we covering in this blog?? Basically, all you need to know about in and out of webhooks, we are going to cover what, why and how of webhooks.
Just let's start without wasting time!
Before going on to know what a webhook is, let’s have a look at the problems we might face without them. Suppose you own an e-commerce business and you have built a website for it where people can add items to their cart, make a payment, and complete their order.
That's their part; as a developer, suppose you have used Stripe for payment service. Now you need to know if the payment is done or not. For this, you might consider polling Stripe to confirm if the payment has been completed. Completing payment might take some time, and we don’t know how much that is. So polling becomes an inefficient approach as we don’t know how many times we are going to ask the Stripe server whether that payment is completed or not.
Here comes the saviour, THE WEBHOOK!
What is a Webhook?
Webhook is a powerful tool that enables real-time communication and data integration between two different services/applications. It is event-driven, which means that whenever an event occurs in one application, it can send real-time data to another application.
Why use Webhook?
Webhooks help us to avoid polling, which is a sort of wastage of resources. What we basically do is expose an endpoint to other services, which can call that endpoint when a particular event occurs.
API: client pulls data when it wants
Webhook: server pushes data when an event happens
Let’s see the implementation for better understanding.
Implementation of Webhooks
1. Initialise the project
First, create a new directory for your project
mkdir webhooks
cd webhooks
mkdir app-A
mkdir app-B2. Set up app-A
cd app-A
npm init -y
npm i expressCreate an index.js file and write the following code in it.
import express from 'express';
const app = express();
const PORT = process.env.PORT || 3000;
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
app.get('/', (req, res) => {
res.send('Hello from App A!');
});
app.post('/webhook', (req, res) => {
// Do something with the webhook data
console.log('Webhook received:', req.body);
res.status(200).send('Webhook received!');
});
app.listen(PORT, () => {
console.log(`App A is running on http://localhost:${PORT}`);
});3. Set up app-B
cd app-B
npm init -y
npm i express axiosCreate an index.js file and write the following content in it.
import express from 'express';
import axios from 'axios';
const app = express();
const PORT = process.env.PORT || 3001;
const webhookData = []; // { url: string, events: string[] }[]
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
app.get('/', (req, res) => {
res.send('Hello from App B!');
});
app.post('/register-webhook', (req, res) => {
// Simulate registering a webhook
const { url, events } = req.body;
webhookData.push({ url, events });
console.log('Registered webhook with data:', req.body);
res.status(200).send('Webhook registered!');
});
app.post('/trigger-webhook', (req, res) => {
// Simulate triggering webhooks
const { event, payload } = req.body;
webhookData.forEach(webhook => {
if (webhook.events.includes(event)) {
console.log(`Triggering webhook at ${webhook.url} with payload:`, payload);
axios.post(webhook.url, payload).catch(err => {
console.error(`Error triggering webhook at ${webhook.url}:`, err.message);
});
}
});
res.status(200).send('Webhooks triggered!');
});
app.listen(PORT, () => {
console.log(`App B is running on http://localhost:${PORT}`);
});4. Register the webhook
Now we are registering a webhook on application B, so that application B will call the webhook when a particular event triggers.
Now the webhook is registered successfully for paymentDone and paymentFailed events. So when either payment is done or a failure, application B will make a call to http://localhost:3000/webhook.
5. Trigger a webhook
Suppose application B received a payment event, and now it’s calling the webhook of application A, which was registered earlier for payment events.
Real-world webhook implementations:
GitHub webhooks for CI
Razorpay for Indian payments
Slack slash commands + event subscriptions
Shopify order updates
Till now, we have covered basic webhook, but real real-world webhook is a lot more than this. It includes retry mechanisms, security, etc.
Retry mechanism in a Webhook.
Now that we know how to trigger a basic webhook, let’s make this more realistic by adding retries, which are essential in real-world integrations.
Get kajutakli’s stories in your inbox
Join Medium for free to get updates from this writer.
Update the index.js file in application B to handle failures and use a retry mechanism.
import express from 'express';
import axios from 'axios';
const app = express();
const PORT = process.env.PORT || 3001;
const webhookData = []; // { url: string, events: string[] }[]
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
app.get('/', (req, res) => {
res.send('Hello from App B!');
});
app.post('/register-webhook', (req, res) => {
// Simulate registering a webhook
const { url, events } = req.body;
webhookData.push({ url, events });
console.log('Registered webhook with data:', req.body);
res.status(200).send('Webhook registered!');
});
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
const sendWithRetry = async (url, payload, retries = 1, baseDelayMs = 1000) => {
let attempt = 0;
while (attempt <= retries) {
try {
attempt++;
console.log(`Attempt ${attempt} to send webhook to ${url}...`);
const response = await axios.post(url, payload);
console.log(`Successfully triggered webhook at ${url}: ${response.status}`);
return true;
} catch (err) {
const status = err.response?.status;
const msg = status
? `HTTP ${status}`
: err.message;
console.error(`Attempt ${attempt} failed for ${url}: ${msg}`);
if (attempt > retries) {
console.error(`No more retries left for ${url}. Giving up.`);
return false;
}
const delay = baseDelayMs * attempt; // Linear backoff
console.log(`Retrying webhook at ${url} in ${delay}ms...`);
await sleep(delay);
}
}
return false;
};
app.post('/trigger-webhook', (req, res) => {
// Simulate triggering webhooks
const { event, payload } = req.body;
webhookData.forEach(webhook => {
if (webhook.events.includes(event)) {
sendWithRetry(webhook.url, payload, 3, 500);
}
});
res.status(200).send('Webhooks triggered!');
});
app.listen(PORT, () => {
console.log(`App B is running on http://localhost:${PORT}`);
});How the output will look after the retry:
Responsibilities of application A
Validate signature
Do minimal work
Return 2xx fast
Push heavy stuff to a background worker / queueAlways treat webhook handlers as idempotent, because retries may deliver the same event multiple times.
The one thing we haven’t talked about is handling security in webhooks. You can go through the following article, which will give you a better understanding of securities in webhooks.
The article mentions three ways to secure webhooks:
- Verify the Source
- IP Whitelisting
- Secret Token
- Validate Payload Integrity
https://medium.com/@faizan.ahmad.info/how-to-implement-a-secure-webhook-in-node-js-7c00e1314f3f
I hope you have learned something about webhooks!! This is my first-ever blog. I appreciate your feedback and the mistakes I can avoid in the next blogs.





