- Products
- Solutions Use casesBy industry
- Developers
- Resources Connect
- Pricing
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:
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.
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.
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.
Since we are using Hosted Authentication, you can read more Nylas’ Hosted Authentication. The two steps we need to complete are:
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 earlierREDIRECT_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:
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.
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 earlierNYLAS_CLIENT_SECRET
is the Nylas application client secretNYLAS_API_KEY
is the Nylas application keyNow we’ve built out the backend authentication flow to receive the Nylas callback and retrieve the user’s access token.
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.
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:
We are going to create a table calendar-events to store calendar events:
Now we’ve created a persistence layer to store 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.
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:
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.
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!
Ram loves teaching, building and exploring technologies. He is passionate about empowering developers to ship amazing products to market as fast as possible ????. Ram is excited to share knowledge and help others. He’s a Relaxed Tomato ????.