How to sync Google and Outlook Calendars in your App

Learn how to sync Google and Outlook calendars in your app using Nylas Calendar API functionality.

how to sync google and outlook calendars in your app

The code in the blog post has been updated to work with Nylas API V3.


Intro

This post will build out functionality to sync Google and Outlook calendars. This will allow your application to combine calendars from different providers. Consider checking our blog post explaining calendar sync and why it matters. We also shared all code on our Nylas Samples repository.

You can also checkout our past Livestream on building calendar sync functionality:

Prerequisites

  • Sign up here if you need to create a Nylas account for free!
    • You will need to create a new Nylas application to connect Google and Outlook users in your app
  • We’ll be using Vercel to:
    • to build the front end using Next.js
    • to deploy a serverless endpoint to retrieve the user’s credentials and
    • to retrieve recent calendar events, more on this soon!
  • We’ll be using Supabase to persist calendar event data for all connected accounts to display on the frontend

Environment

We’ll be building using Next.js. Next.js is a framework using React and provides you with building blocks to create web applications. We previously talked about Next.JS when we posted about how to create Nylas webhooks using Vercel. If you haven’t used Next.js before, check out the system requirements in the docs. If you want to learn more about Next.js, check out their getting started guide.

Setup Google and Azure Apps

We’ve previously covered setting up your Google and Azure apps:

We need to setup both apps so we can sync Google and Outlook calendars.

Now we’ve covered how to create your Google and Azure accounts.

Test out Google and Azure App Integrations using Nylas Dashboard

Before we build out our application, let’s test out connecting an account flow for both providers. Here is an example of connecting your Outlook account by clicking Add test grant using the Nylas Dashboard to test out the integration:

Complete this step for both a Google and Microsoft account to ensure the integrations are setup correctly. We’ll be building out the authentication into our application that will work similar to this.

Now we’ve tested connecting a Google and Microsoft account using the Nylas dashboard.

Set up Hosted Authentication Flow

Frontend: Receiving User Inputs for Authentication

Since we are using Hosted Authentication, you can read more Nylas’ Hosted Authentication. The two steps we need to complete are:

  1. create a text input to accept a user email and
  2. call Nylas’ oauth/authorize endpoint to start the authentication flow for us.

Here is a code sample to start the authentication flow on the frontend:

// sync-google-calendar-outlook/pages/index.jsx

import { Page, ButtonGroup, Button, Text, Input } from '@geist-ui/core'
import { useRef } from 'react';

const Home = () => {
  const inputRef = useRef(null);

  const onConnectAccount = () => {
    const emailToAuthenticate = inputRef.current.value;

    // Nylas Application Client ID
    const CLIENT_ID = 'CLIENT_ID';
    // REDIRECT_URI is our endpoint that Nylas call's with a one-time code to retrieve the access token
    const REDIRECT_URI = 'REDIRECT_URI';

    // Redirect to Nylas’ oauth/authorize endpoint with CLIENT_ID, REDIRECT_URI, 
    // and User Email (emailToAuthenticate)
    window.location = `https://api.nylas.com/oauth/authorize?client_id=${CLIENT_ID}&redirect_uri=${REDIRECT_URI}&login_hint=${emailToAuthenticate}&response_type=code&scopes=calendar.read_only`

  };

  return (
    <Page>
    <Text h1><span style={{ color: '#0070f3' }}>????️ CalSync </span>built with Nylas</Text>
    
    <Input clearable initialValue="" placeholder="Enter your email to start!" ref={inputRef}/>
    <ButtonGroup type="success">
      <Button  onClick={onConnectAccount}>Connect Account</Button>
    </ButtonGroup>
  </Page>
  )
}

export default Home;

A few variables need to be specified:

  • CLIENT_ID is the Nylas application created earlier
  • REDIRECT_URI is your backend that will receive information from Nylas, we will definite this value once we setup the backend (next section)

Here is what the frontend will look like:

frontend authentication flow

Since we already tested hosted authentication using the Nylas Dashboard, we can revisit clicking the Connect Account button in an upcoming section. We first need to create the serverless function on Vercel to receive information from Nylas post-authentication, so we will create that next.

Now we’ve build out the frontend authentication flow using Nylas’ Hosted Authentication.

Backend: Setting up the Nylas Callback endpoint

Once the user authenticates using Hosted Authentication Nylas will call our backend using the REDIRECT_URI provided (mentioned in the previous section). We will set up a serverless function to receive the Nylas callback and retrieve the user’s access token.

We previously went over how to set up a serverless function using Vercel, so check that out if you need more information. Ensure you update the front end with the REDIRECT_URI after creating the serverless function.

With the user’s access token, we can connect to their communication data and receive information like calendar events. Let’s look at the code for receiving the Nylas Callback:

// sync-google-calendar-outlook/pages/api/nylas_callback.js

export default function handler(req, res) {
  console.log('req.query.code', req.query.code)
  const client_id = process.env.NYLAS_CLIENT_ID;
  const client_secret = process.env.NYLAS_CLIENT_SECRET;
  const bearerToken = process.env.NYLAS_API_KEY;
  const code = req.query.code;

  const headers = new Headers();
  headers.append("Content-Type", "application/json");
  headers.append("Accept", "application/json");
  headers.append("Authorization", `Bearer ${bearerToken}`);
  
  const raw = JSON.stringify({
    "code": req.query.code,
    "client_id": client_id,
    "client_secret": client_secret,
    "redirect_uri": redirect_uri,
    "grant_type": "authorization_code"
  });
  
  const requestOptions = {
    method: "POST",
    headers: headers,
    body: raw,
    redirect: "follow"
  };

  fetch("https://api.us.nylas.com/v3/connect/token", requestOptions)
    .then(response => {
      return response.json();
    })
    .then(userData => {
      const email = userData.email_address;
      const accessToken = userData.access_token;
    })
    .catch(error => console.log('error', error));
}

A few variables need to be specified using Vercel environment variables:

  • NYLAS_CLIENT_ID is the Nylas application ID created earlier
  • NYLAS_CLIENT_SECRET is the Nylas application client secret
  • NYLAS_API_KEY is the Nylas application key

Now we’ve built out the backend authentication flow to receive the Nylas callback and retrieve the user’s access token.

Sync Calendars

Let’s consider the steps for syncing multiple calendars. In this section, we are going to consider how we can retrieve and store calendar events.

Storage: Setup Supabase (optional)

We are going to use Supabase to store calendar events. This step is optional as you can also retrieve calendar events by persisting the access token and retrieving events on page load.

Let’s link Vercel to a new Supabase project, sync-google-calendar-outlook so we can store all users’ calendars events:

link vercel to supabase

We are going to create a table calendar-events to store calendar events:

create table in supabase to store calendar events

Now we’ve created a persistence layer to store calendar events.

Backend: Retrieve and Persist Calendar Events

After the user authenticates, let’s retrieve their calendar events using the Nylas API and store them so we can show all user’s calendars events. Let’s look at the code for receiving calendar events:

// sync-google-calendar-outlook/pages/api/nylas_callback.js

// Add global variables to temporarily store email, grantId and bearer token
let email = '';
let grantId = '';
let calendar;

// ... continuing after retrieving userData
.then(response => {
  const userData = JSON.parse(response);
  email = userData.email_address;
  grantId = userData.grant_id;

  // We are passing in the calendar_id as primary to fetch the user's primary calendar
  return fetch(`https://api.us.nylas.com/v3/grants/${grantId}/calendars/primary`, {
    method: 'GET',
    headers,
    redirect: 'follow'
  });
})
.then(response => response.json())
.then(calendars => {
  const calendar = response.data

  // Retrieve calendar events starting now and starting before 7 days
  const now = Math.floor(Date.now()/1000);
  // 604800 is 7 days in seconds
  const future = now + 604800;

  return fetch(`https://api.us.nylas.com/v3/grants/${grantId}/events?limit=50&calendar_id=primary&start=${now}&end=${future}`, {
    method: 'GET',  
    headers: createHeaders(),
    redirect: 'follow'
  })
})
// ... store the calendar events in Supabase
.then(() => {
  // Redirect the user back to the frontend
  return res.status(200).redirect('https://sync-google-calendar-outlook.vercel.app/')
})
....

One thing to remember when creating serverless functions is that the total runtime should be minimal (in this case less than 5 seconds) to avoid timeout. This code sample is sufficient for demonstration purposes, however, I would consider separating out the functions to complete separate tasks (i.e. retrieve access token, persist calendar events).

So now we setup the backend to retrieve and store calendar events each time a user connects their account.

Display Calendars

Let’s setup the frontend to retrieve Calendar events from Supabase and display them. We are going to use react-big-calendar to display all events for the next 7 days. Here is what the code will look like:

import { Page, Button, Text, Input } from '@geist-ui/core'
import { useCallback, useState, useMemo, useEffect } from 'react';
import { Calendar, luxonLocalizer, Views } from 'react-big-calendar'
import { DateTime, Settings } from 'luxon'

import { createClient } from '@supabase/supabase-js'

// TODO: Retrieving Supabase credentials from Vercel environment
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL;
const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY;
const supabase = createClient(supabaseUrl, supabaseKey);

function getDate(str, DateTimeObj) {
  return DateTimeObj.fromISO(str).toJSDate()
}

const defaultDateStr = '2022-11-27'

export default function Home() {
  const defaultTZ = DateTime.local().zoneName;
  const inputRef = useRef(null);
  const [eventData, setEventData] = useState([]);
  const [timezone, setTimezone] = useState(defaultTZ);

  useEffect(async () => {
    const data = await supabase.from('calendar_events').select('*')
    setEventData(data);
  }, [])

  // ... existing onConnectAccount code

  // Create the relevant data to pass to react-big-calendar
  const { defaultDate, getNow, localizer, myEvents, scrollToTime } = useMemo(() => {
    Settings.defaultZone = timezone
    return {
      defaultDate: getDate(defaultDateStr, DateTime),
      getNow: () => DateTime.local().toJSDate(),
      localizer: luxonLocalizer(DateTime),
      myEvents: [
        ...eventData.map(e => ({
          id: e.event_id,
          title: e.event_title,
          start: new Date(e.event_start * 1000),
          end: new Date(e.event_end * 1000),
        }))
      ],
      scrollToTime: DateTime.local().toJSDate(),
    }
  }, [timezone]);

  return (
    <Page>
    { /* ... existing components */ }
      <div>
	{ eventData.length !== 0 && (
        <Calendar
          defaultDate={defaultDate}
          defaultView={Views.WEEK}
          events={[
            ...eventData.map(e => ({
              id: e.event_id,
              title: e.event_title,
              start: new Date(e.event_start * 1000),
              end: new Date(e.event_end * 1000),
            }))
          ]}
          getNow={getNow}
          localizer={localizer}
          scrollToTime={scrollToTime}
        />
      )}
    </div>
  </Page>
  )
}

Let’s take a look at what that looks like:

google and outlook calendars synced

With the backend and frontend complete, we can use the Connect Account button to add multiple Google and Microsoft accounts to display the calendar events.

Now we’ve added the frontend code to retrieve the calendar events across different accounts (Google + Outlook) and display them.

Build Time!

We just built out full functionality to sync google and outlook calendars. Continue building with Nylas and learn more by visiting the documentation on the Nylas Calendar API. Sign up here if you need to create a Nylas account for free!

You can sign up Nylas for free and start building!

You May Also Like

Transactional Email APIs vs Contextual Email APIs
Best email tracker
Find the best email tracker and elevate your app’s email game
How to create and read Webhooks with PHP, Koyeb and Bruno

Subscribe for our updates

Please enter your email address and receive the latest updates.