Export calenders with iCal

I created a basic version of this feature using a cloudflare worker and the graphql API!

It doesn’t currently support recurring events, but otherwise, it works perfectly! You will need to edit the graphql query to work with your calendar database (my timed events are “meetings” and my all-day events are “timelines”). Serve the worker from a custom domain, and then you can have a live link to an ICS file!

import ical from 'ical-generator';

const ENDPOINT = ''; // graphql endpoint and token
const TOKEN = '';

export default {
	async fetch(request) {
		try {
			const query = `
	  {
		findMeetings {
		  id,
		  name,
		  description {text},
		  dates {start, end},
		  location,
		  repeatSettings
		},
		findTimelines {
		  id,
		  name,
		  description {text},
		  date {start, end}
		}
	  }`;

			const response = await fetch(ENDPOINT, {
				method: 'POST',
				body: JSON.stringify({ query }),
				headers: {
					'Content-Type': 'application/json',
					Authorization: `Token ${TOKEN}`,
				},
			});
			const data = await response.json();

			const cal = ical({ name: 'Meeting and Timeline Schedule' });

			// Handle findMeetings
			data.data.findMeetings.forEach((meeting) => {
				cal.createEvent({
					id: `meeting-${meeting.id}`,
					start: new Date(meeting.dates.start),
					end: new Date(meeting.dates.end),
					summary: meeting.name,
					description: meeting.description.text,
					location: meeting.location || '',
				});
			});

			// Handle findTimelines as all-day events
			data.data.findTimelines.forEach((timeline) => {
				cal.createEvent({
					id: `timeline-${timeline.id}`,
					start: new Date(timeline.date.start),
					end: new Date(timeline.date.end),
					summary: timeline.name,
					description: timeline.description.text,
					allDay: true,
				});
			});

			const icsFile = cal.toString();
			return new Response(icsFile, {
				headers: {
					'Content-Type': 'text/calendar',
					'Content-Disposition': 'attachment; filename="meetings-and-timelines.ics"',
				},
			});
		} catch (error) {
			console.error(error);
			return new Response(JSON.stringify({ message: 'Internal Server Error' }), {
				status: 500,
				headers: {
					'Content-Type': 'application/json',
				},
			});
		}
	},
};
3 Likes