- Products
- Solutions Use casesBy industry
- Developers
- Resources Connect
- Pricing
This blog walks you through how to efficiently manage and display real-time calendar availability using the Nylas API.
By implementing the steps outlined in this blog, you can build a robust scheduling tool that enhances user experiences and streamlines operations.
Managing availability for scheduling on multiple calendars—whether for healthcare providers, tutors, interviewing or other professionals—can be complex and resource-intensive. This blog provides a comprehensive solution for efficiently querying and managing availability using the Nylas API. With real-world examples and code snippets, you’ll learn how to build a scalable, real-time availability scheduler tailored for your application.
This solution helps you efficiently expose calendar availability for tens, hundreds, or even thousands of accounts by integrating with the Nylas API. It provides a clear setup guide, from authentication to implementation, and includes both high-level concepts and practical steps with code examples. Whether you’re a product manager looking for feasibility insights or an engineer ready to build, this guide is for you.
An availability scheduler, or calendar, visually displays when a person or resource is available for meetings or appointments. It’s commonly used for managing resource allocation and booking online appointments, reservations, and events.
These calendars typically present days, weeks, or months in a grid format, with color coding to indicate availability. For example, green may represent an open time slot, while red shows a blocked period. Availability calendars help prevent scheduling conflicts, enhance time management, and simplify the scheduling process.
They are used for:
Purpose: Allow users (e.g., healthcare providers, coaches, tutors) to connect their email accounts, enabling your app to fetch calendar data.
Code example:
import 'dotenv/config'
import express from 'express'
import Nylas from 'nylas'
const config = {
clientId: process.env.NYLAS_CLIENT_ID,
callbackUri: "http://localhost:3000/oauth/exchange",
apiKey: process.env.NYLAS_API_KEY,
apiUri: process.env.NYLAS_API_URI
}
const nylas = new Nylas({
apiKey: config.apiKey,
apiUri: config.apiUri
})
const app = express()
const port = 3000
// Route to initialize authentication
app.get('/nylas/auth', (req, res) => {
const authUrl = nylas.auth.urlForOAuth2({
clientId: config.clientId,
provider: 'google',
redirectUri: config.callbackUri,
loginHint: 'email_to_connect',
})
res.redirect(authUrl)
})
Purpose: Surface available time slots for end-users in your application.
Code example:
import 'dotenv/config'
import Nylas from 'nylas'
const NylasConfig = {
apiKey: process.env.NYLAS_API_KEY,
apiUri: process.env.NYLAS_API_URI,
}
const nylas = new Nylas(NylasConfig)
const email = process.env.EMAIL
async function getCalendarAvailability() {
try {
const calendar = await nylas.calendars.getAvailability({
requestBody: {
startTime: 1630435200,
endTime: 1630521600,
duration_minutes: 30,
participants: [{email}] //if empty, Nylas uses the primary calendar id (participant email needs to be authenticated to your Nylas app)
}
})
console.log('Calendar:', calendar)
} catch (error) {
console.error('Error to create calendar:', error)
}
}
getCalendarAvailability()
Store the results in Redis or your cache of choice (be sure to structure your cache so it’s efficient for batch querying).
// Store availability data
redisClient.set('[email protected]',JSON.stringify(availability);
// Retrieve availability data
redisClient.get('[email protected]', (err, data) => { const availability = JSON.parse(data); });
Purpose: Keep your application up-to-date with any changes.
Register webhooks to monitor calendar changes:
import 'dotenv/config'
import Nylas, { WebhookTriggers } from "nylas"
const NylasConfig = {
apiKey: process.env.NYLAS_API_KEY,
apiUri: process.env.NYLAS_API_URI,
}
const nylas = new Nylas(NylasConfig)
const createWebhook = async () => {
try {
const webhook = await nylas.webhooks.create({
requestBody: {
triggerTypes: [WebhookTriggers.EventCreated, WebhookTriggers.EventUpdated, WebhookTriggers.EventDeleted],
webhookUrl: process.env.WEBHOOK_CALLBACK_URL,
description: "My first webhook",
notificationEmailAddress: [process.env.NOTIFICATION_EMAIL],
}
})
console.log("Webhook created:", webhook)
} catch (error) {
console.error("Error creating webhook:", error)
}
}
createWebhook()
/* Example output:
Webhook created: {
requestId: '1',
data: {
id: '2',
description: 'My first webhook',
triggerTypes: [ 'event.created' ],
webhookUrl: 'your-webhook-callback-url',
webhookSecret: 'webhook-secret-to-store',
status: 'active',
notificationEmailAddresses: null,
statusUpdatedAt: 1710964914,
createdAt: 1710964914,
updatedAt: 1710964914
}
}
*/
Purpose: Update cache in real-time.
Set up an Express route to process webhook payloads:
import "dotenv/config";
import express from "express";
import Nylas from "nylas";
import crypto from 'crypto';
const config = {
clientId: process.env.NYLAS_CLIENT_ID,
apiKey: process.env.NYLAS_API_KEY,
apiUri: process.env.NYLAS_API_URI,
};
const nylas = new Nylas({
apiKey: config.apiKey,
apiUri: config.apiUri, // "https://api.us.nylas.com" or "https://api.eu.nylas.com"
});
const app = express();
const port = 3000;
// start the server
app.listen(port, () => {
console.log(`Server is running on port ${port}`);
});
// route to respond to Nylas webhook creation webhook with challenge parameter
app.get("/callback/nylas", (req, res) => {
if (req.query.challenge) {
console.log(`Received challenge code! - ${req.query.challenge}`);
console.log(`Now returning challenge code! - ${req.query.challenge}`);
// we need to enable the webhook by responding with the challenge parameter
return res.send(req.query.challenge);
}
});
// route to receive webhook events after creating webhook
app.post("/callback/nylas", (req, res) => {
const nylasSignature = 'x-nylas-signature';
const hashAlgorithm = 'sha256'
/* secret is storing the webhook secret in-memory for demo purposes
TODO: Store the secret and retrieve as needed to verify the webhook signature
*/
const webhookSecret = process.env.WEBHOOK_SECRET;
console.log('==========Webhook log start==========');
console.log(JSON.stringify(req.body.data));
// Verify the webhook signature
const signature = req.headers[nylasSignature];
const digest = crypto
.createHmac(hashAlgorithm, webhookSecret)
.update(req.body.data)
.digest('hex')
const isValidWebhook = digest === nylasSignature
console.log({isValidWebhook})
console.log('==========Webhook log end==========\n');
// Responding to Nylas is important to prevent the webhook from retrying
return res.status(200).end();
});
Store the results in Redis or your cache of choice (be sure to structure your cache so it is efficient for batch querying, like above).
Purpose: Efficiently query cache to serve many users’ availability at the same time.Use Redis’ mget command to retrieve multiple keys at once:
const queryBatchAvailabilityRedis = async (userIds, startTime, endTime) => {
const pipeline = redisClient.pipeline();
userIds.forEach(userId => pipeline.get(userId));
const cacheResults = await pipeline.exec();
const results = {};
cacheResults.forEach(([err, data], index) => {
const userId = userIds[index];
if (err || !data) {
results[userId] = [];
return;
}
const availability = JSON.parse(data);
results[userId] = availability.filter(slot => {
const slotStart = new Date(slot.start).getTime();
const slotEnd = new Date(slot.end).getTime();
const queryStart = new Date(startTime).getTime();
const queryEnd = new Date(endTime).getTime();
return slotStart >= queryStart && slotEnd <= queryEnd;
});
});
return results;
};
// Example Usage:
const userIds = ['[email protected]', '[email protected]', '[email protected]'];
const startTime = '2024-01-01T10:00:00Z';
const endTime = '2024-01-01T15:00:00Z';
queryBatchAvailabilityRedis(userIds, startTime, endTime).then(batchAvailability => {
console.log(batchAvailability);
});
Chunk the queries: If you’re querying hundreds of users, process them in chunks to avoid overwhelming memory or Redis pipelines.
const chunkArray = (array, size) => {
const chunks = [];
for (let i = 0; i < array.length; i += size) {
chunks.push(array.slice(i, i + size));
}
return chunks;
};
const userChunks = chunkArray(userIds, 50); // Process 50 users at a time
for (const chunk of userChunks) {
const results = await queryBatchAvailabilityRedis(chunk, startTime, endTime);
console.log(results);
}
Purpose: Offer in-app appointment booking.
import 'dotenv/config'
import Nylas from 'nylas'
const NylasConfig = {
apiKey: process.env.NYLAS_API_KEY,
apiUri: process.env.NYLAS_API_URI,
}
const nylas = new Nylas(NylasConfig)
const now = Math.floor(Date.now() / 1000) // Time in Unix timestamp format (in seconds)
async function createAnEvent() {
try {
const event = await nylas.events.create({
identifier: process.env.NYLAS_GRANT_ID,
requestBody: {
title: 'Tutoring Session',
when: {
startTime: now,
endTime: now + 3600,
},
location:'Class Room E475',
conferencing: {CONFERENCING_DOCS},
participants: [{
email: "[email protected]",
name: "Student Name"
}]
},
queryParams: {
calendarId: process.env.CALENDAR_ID,
},
})
console.log('Event:', event);
} catch (error) {
console.error('Error creating event:', error)
}
}
createAnEvent()
Purpose: Any booked time slots will not show as free once the cache is updated.
Purpose: Accurately display available time slots to end users.
Managing calendar availability across multiple accounts doesn’t have to be overwhelming. With the Nylas API, you can streamline the process, whether you’re working with a handful of schedules or thousands. By following this guide, you’ve learned how to authenticate, query availability, and build a real-time availability scheduler tailored to your needs.
Whether you’re developing scheduling solutions for healthcare providers, tutors, or interviewers, the Nylas API offers the tools to build a robust, scalable solution with ease. Sign up for free and start building your availability scheduler today.