Google Calendar integration
Prerequisites
- This was done using a G-Suite account, I'm not sure this can be done with a normal Gmail account.
- a Google cloud project
- NodeJS and NPM/Yarn installed
- Basic knowledge of JavaScript and ES6
Introduction
A client asked me to integrate a scheduling system into her website, to facilitate her appointments. I told her it would be an expensive solution written by hand as she is only a small company. However I'm always up to learn new stuff and looked into ways to make it happen. One of the first things I thought about and is part our daily lives is Google Calendar. It's easy and robust. So I took a look at the APIs and what and/or if it was possible to integrate it.
Headaches and solutions (this part can be skipped)
With the NPM package googleapis
you have access to various Google services. However in my opinion the APIs are documented in a strange way, various parts in one place other parts in another, I wasn't always sure if parts were really related to the NPM package.
It took me a long time to understand authentication part. After trying the APIs with OAuth 2.0 my application was working. But the token was short lived. I didn't want the headache of repeating the authentication process. I thought that there should be some other way so I looked into API keys. Google provides this but not for every API, but calendar wasn't supported. The solution finally came after lots of searching and reading. A Google service account. That's what I'm going to share with you guys today. It's actually pretty darn easy.
Setting up a Google Service Account (in the Google Cloud Console)
Obs. This article will not go in to depth setting up a Google Cloud Project
In the console go to IAM And Management -> Service accounts
and create a new service account. Give it a name, an ID (this is also an email we'll need later on) and a description. Click Create. This account doesn't need permissions. Click Continue. This account doesn't need a service account user nor admin. But we'll need a JSON key. A file will be downloaded that we'll need later on.
The calendar
We'll now add the service account email (ID) to the calendar. Add it to the Calendar. Click here to know how to. The email should have the following permission Make changes to events
.
The calendar ID can be found here, we'll also need it.
Well that was easy wasn't it, not everything needs to be bureaucratic.
The fun part
Now we'll run some basic NodeJS tests, make new project dir with npm init -y
and install npm i -s googleapis date-fns
to the project. Date-fns we'll use to format and manipulate dates. Copy the credentials json file in to the root of the project directory.
Let's create an get.js with the following code:
const { addWeeks } = require('date-fns')
const { google } = require('googleapis')
const credentials = require('<CREDENTIALS_FILE>.json')
const scopes = ['https://www.googleapis.com/auth/calendar']
const client = google.auth.getClient({
credentials,
scopes,
})
client.subject = '<SERVICE_ACCOUNT_EMAIL>'
const calendar = google.calendar({ version: 'v3', auth: client })
calendar.events.list(
{
calendarId: '<CALENDAR_ID>',
timeMin: new Date().toISOString(),
timeMax: addWeeks(new Date(), 1).toISOString(), // Let's get events for one week
singleEvents: true,
orderBy: 'startTime',
},
(err, res) => {
if (err) {
console.log(`The API returned an error: ${err}`)
}
// console.log(res.data.items) // All data
const appointments = res.data.items.map((appointment) => ({
start: appointment.start.dateTime || appointment.start.date,
end: appointment.end.dateTime || appointment.end.date,
id: appointment.id,
status: appointment.status,
creator: appointment.creator,
description: appointment.description,
}))
console.log(appointments)
},
)
and let's also create a new calendar event:
const { addMinutes } = require('date-fns')
const { google } = require('googleapis')
const credentials = require('<CREDENTIALS_FILE>.json')
const scopes = ['https://www.googleapis.com/auth/calendar']
const client = google.auth.getClient({
credentials,
scopes,
})
client.subject = '<SERVICE_ACCOUNT_EMAIL>'
const calendar = google.calendar({ version: 'v3', auth: client })
calendar.events.insert(
{
calendarId: '<CALENDAR_ID>',
resource: {
start: {
dateTime: new Date().toISOString(),
timeZone: '<TIMEZONE>',
},
end: {
dateTime: addMinutes(new Date(), 60).toISOString(),
timeZone: '<TIMEZONE>',
},
summary: 'Test event',
status: 'confirmed',
description: 'Test description',
},
},
(err, event) => {
if (err) console.log('Error', err)
console.log(event.data)
},
)
This was a lot of work to find and figure out. I learned a lot, especially that it actually wasn't that hard!
This is my first post, help me with my writing skills and share your thoughts!
Hope it's of good use, happy hacking!
Top comments (4)
First: this article was super helpful and got me 95% of the way to a working solution. So thank you so much for posting it!
Here's what didn't work for me:
When I ran the posted code, I would get this error:
...which, after googling it, seems like an auth or API version error.
It could be related (or not) to the fact that I am not using a G-Suite account. So for anyone wondering how to make it work for a regular google account, here's how!
What I did is I replaced this:
...with this:
(Source: googleapis.dev/nodejs/googleapis/l...)
So in my case, there was no need to
require()
the credentials file (asgoogle.auth.GoogleAuth()
does that for us), so I removed this line:...and I also removed this line:
...as the service account email already exists in the credentials file (under the
"client_email"
key).So the end-result is even more simple, thanks to
google.auth.GoogleAuth()
.And it might work for G-Suite accounts too. (So if anyone with a G-Suite account wants to try this, please post here to let us know if this worked for you too.)
Got the same issue. this change fixed the issue. Awesome tip.
Very helpful article.
very helpful, thank you.