DEV Community

maarteNNNN
maarteNNNN

Posted on • Edited on

Google Calendar integration with NodeJS without OAuth 2.0

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)
  },
)

Enter fullscreen mode Exit fullscreen mode

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)
  },
)
Enter fullscreen mode Exit fullscreen mode

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)

Collapse
 
jdunk profile image
jdunk

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:

The API returned an error: TypeError: authClient.request is not a function
Enter fullscreen mode Exit fullscreen mode

...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:

const client = google.auth.getClient({
  credentials,
  scopes,
})
Enter fullscreen mode Exit fullscreen mode

...with this:

const client = new google.auth.GoogleAuth({
  keyFile: '<CREDENTIALS_FILE>.json',
  scopes,
})
Enter fullscreen mode Exit fullscreen mode

(Source: googleapis.dev/nodejs/googleapis/l...)

So in my case, there was no need to require() the credentials file (as google.auth.GoogleAuth() does that for us), so I removed this line:

const credentials = require('<CREDENTIALS_FILE>.json')
Enter fullscreen mode Exit fullscreen mode

...and I also removed this line:

client.subject = '<SERVICE_ACCOUNT_EMAIL>'
Enter fullscreen mode Exit fullscreen mode

...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.)

Collapse
 
balindersingh profile image
Balinder Singh

Got the same issue. this change fixed the issue. Awesome tip.

Collapse
 
balindersingh profile image
Balinder Singh

Very helpful article.

Collapse
 
azerty507 profile image
azerty507

very helpful, thank you.