I’d already built one calendar integration - Friday talks to Google Calendar - and the thought of vendoring a second, completely different SDK just to add Outlook support was unappealing. Google Calendar, Microsoft Graph, and CalDAV each model events, sync, and auth differently, and none of them agree on shape. So I built the abstraction I actually wanted: one API, three providers underneath it.
One API, swappable providers
sdk.calendars.list(), sdk.events.create(), sdk.availability.freeBusy() - these calls work identically no matter which provider is registered. Swapping createGoogleProvider for createMicrosoftProvider is the only code that changes; the rest of the calling code doesn’t know or care which calendar backend it’s actually talking to.
Six packages, not one
The core package (@calendar-sdk/core) holds the unified services, a StorageAdapter interface, hooks, and a retrying transport layer - but it doesn’t ship any provider by default. Each provider (@calendar-sdk/google, @calendar-sdk/microsoft, @calendar-sdk/caldav) is its own package, so a consumer who only needs Google Calendar doesn’t pull in Microsoft Graph’s dependencies. OAuth flow, token refresh, and encrypted token-set storage live in a standalone @calendar-sdk/auth package, reusable on its own outside the calendar context entirely. A @calendar-sdk/mock provider gives an in-memory implementation for tests and examples with zero network calls or credentials required.
Bring your own everything
The SDK deliberately doesn’t own persistence or OAuth credentials - callers implement StorageAdapter against their own database and supply their own client ID/secret. That’s the actual value proposition next to a paid unified-calendar API: no vendor lock-in on where tokens live, at the cost of a bit more setup than a hosted service would require.
Key decisions
- Splitting into 6 packages instead of one with peer dependencies - a consumer using only Google Calendar shouldn’t need Microsoft Graph’s SDK in their
node_modules; independent versioning per provider also means a CalDAV-specific fix doesn’t force a version bump on the Google adapter StorageAdapteras an interface, not a bundled database - the SDK unifies calendar APIs, not databases; forcing a specific storage layer would make it a worse fit for every app that already has its own persistence story- A mock provider as a first-class package, not a test fixture - shipping the mock as
@calendar-sdk/mockmeans examples and quickstarts can run with zero credentials and zero network calls, which matters a lot for anyone evaluating the SDK before wiring up real OAuth
Built with
- TypeScript - pnpm workspace, 6 packages
- Google Calendar API, Microsoft Graph API, CalDAV - provider adapters
- OAuth - standalone auth package with token refresh
- Astro + Starlight - documentation site
- GitHub Actions - CI matrix across packages