-->
October 16, 2024
You might have come across a situation where you have scheduled an event in google calendar, and when it is updated, you want to perform some action such as informing slack channel or any other communication channel. The event update can include:
In this article, we will explore how we can inform our system when an event is updated in google calendar.
Before we dive into writing code, you can head over to google calendar documentation here. The documentation here shows how can we watch for google calendar events using the calendar watch API.
In this article, we would not be implementing the webhook using the API provided above. Instead, we will use an npm package called googleapis. This package contains extensive support for using google APIs in the node.js applications.
You can install the package with npm or yarn.
yarn add googleapis
Import the package into the node.js application
const google = require("googleapis");
We start by creating a project in the google developer console and setting OAuth credentials which we will need when using googleapis
package. You can visit here to know how we can set credentials for the project in the google developer console. Store the credentials in a .env
file.
GOOGLE_CLIENT_ID=***********************
GOOGLE_CLIENT_SECRET=********************************
Next, obtain end-user authorization grants for use with other Google APIs by using OAuth2
imported from google.auth
.
const { OAuth2 } = google.auth
Create a new OAuth2Client
object by using:
const oAuth2Client = new OAuth2(
process.env.GOOGLE_CLIENT_ID,
process.env.GOOGLE_CLIENT_SECRET
);
Create a new calendar instance
const calendar = google.calendar({
version: "v3",
auth: oAuth2Client
});
Once the calendar instance is created, we need to create a watch instance for the google calendar using Google Calendar Push Notifications API. The push notifications API lets us watch for the calendar events when they are updated. To watch for calendar events, we need to provide the following properties in the request object:
id
property which uniquely identifies the notification channeltype
property which is set to webhook
address
property which specifies the callback URL to which the API sends a POST request informing that the calendar is updatedWhen we are using the node.js application locally, the server might be running at http://localhost:3000
. We need to expose our local port to the internet using ngrok
.
To install ngrok
globally in the system, type the following command in the terminal.
yarn add global ngrok
When installed, run the following command
ngrok http 3000
This command will create a reverse proxy for the local port.
Remember that ngrok
reverse proxy expires after 2 hours when using the free tier. This ngrok
URL will be used in the address property. We create a watch event using the code below:
await calendar.events.watch({
resource: {
id: uuidv4(),
type: "web_hook",
address: `${process.env.SERVER_URL}/api/v1/calendar/webhook`
},
});
When the calendar is updated, the watch event informs our application by sending a POST request to the call back URL which we specified in the address
property when creating a watch event. The watch event notification does not include a message body, rather it only includes some headers which are:
X-Goog-Channel-ID
which is the id that we set to uniquely identify the notification channel.X-Goog-Resource-ID
which identifies the watched resource.X-Goog-Resource-State
which specifies the new resource state. The state value can be sync
, exists
, or not_exists
.X-Goog-Resource-URI
which is an API-version-specific identifier for the watched resource i.e. the google calendar.app.post('/calendar/webhook', async (req, res) => {
const resourceId = request.headers['x-goog-resource-id'];
const channelToken = request.headers['x-goog-channel-token'];
const channelId = request.headers['x-goog-channel-id'];
const resourceState = request.headers['x-goog-resource-state'];
// As there is no message body, we need to manually fetch events from
// the calendar and look for any changes
const calendar = google.calendar({
version:"v3",
auth: < oAuthClient > // replace this value with the one created at the start
})
const response = await calendar.events.list({
calendarId: "primary",
timeMin: new Date().toISOString(), // fetch events after the current time
singleEvents: true,
showDeleted: "true" // display deleted events if any
});
const events = response.data.items;
console.log({ events })
res.json({
status: 200,
message: "Webhook called"
});
});
In the above code, we have created a route that handles the webhook. Let’s move on to exploring the code.
google.calendar
and passing oAuthClient
what we have created earlier.calendar.events.list
. We are also showing deleted events if there are any.For this article, we are only displaying the events on the console but for practical use, we need to check which events are changed in the calendar. The implementation might vary from case to case. A simple implementation would be storing the event in the local database when it is created in the google calendar. When the calendar is updated, we will look into our database and fetch the event created earlier. Then we map through all the google calendar events which are returned from calendar.events.list
and check if the event id matches with the one present in our database. If yes, we can update the event with the new data. This way, we can make better use of the google calendar webhook.
We have seen how we can implement a webhook that will inform our application when there is any update in the google calendar. We make a route to handle the webhook and perform any required actions.
Happy coding !!!