Skip to main content
Version: 2.1.8

Intro

WEBDAV, Web Distributed Authoring and Versioning, is an extension of the HTTP to allow handling distributed authoring, versioning of various resources.

It's very common to be used for cloud storage(limited support), as well as calendar, contacts information syncing.

Cloud provider support status

Provider nameWEBDAVCALDAVCARDDAV
Apple
Google
Fastmail
Nextcloud
Baikal
ZOHO
DAViCal⛔️
Forward Email⛔️

For more information on cloud providers, go to cloud providers for more information.

Install

yarn add tsdav

or

npm install tsdav

Browser usage

Use the ESM bundle in modern browsers:

<script type="module">
import { createDAVClient } from 'https://unpkg.com/tsdav/dist/tsdav.esm.js';

const client = await createDAVClient({
serverUrl: 'https://caldav.icloud.com',
credentials: {
username: 'YOUR_APPLE_ID',
password: 'YOUR_APP_SPECIFIC_PASSWORD',
},
authMethod: 'Basic',
defaultAccountType: 'caldav',
});

const calendars = await client.fetchCalendars();
console.log(calendars);
</script>

Browser requests to CalDAV/CardDAV endpoints are often blocked by CORS. Prefer running tsdav in a server environment, proxying requests through your backend, or using a custom transport.

Cloudflare Workers

tsdav is compatible with Cloudflare Workers. It automatically detects and uses the native fetch provided by the Workers runtime.

Example (using standard Worker syntax):

import { createDAVClient } from 'tsdav';

export default {
async fetch(request, env) {
const client = await createDAVClient({
serverUrl: 'https://caldav.icloud.com',
credentials: {
username: env.APPLE_ID,
password: env.APPLE_PASSWORD,
},
authMethod: 'Basic',
defaultAccountType: 'caldav',
});

const calendars = await client.fetchCalendars();
return new Response(JSON.stringify(calendars), {
headers: { 'Content-Type': 'application/json' },
});
},
};

Custom Transport (Electron/CORS)

If you are using tsdav in an environment like an Electron renderer process where fetch is restricted by CORS (especially for providers like iCloud or Gmail), you can provide a custom fetch implementation to route requests through a proxy or Electron's main process.

const client = await createDAVClient({
serverUrl: 'https://caldav.icloud.com',
credentials: { ... },
authMethod: 'Basic',
// Custom fetch override
fetch: async (url, options) => {
// Implement your own transport here, e.g., IPC to main process
const response = await window.electronAPI.makeRequest(url, options);
return response;
},
});

The custom fetch should follow the standard Fetch API interface. This is also supported in the DAVClient constructor and most high-level functions.

Basic usage

Import the dependency

import { createDAVClient } from 'tsdav';

or

import tsdav from 'tsdav';

Create Client

By creating a client, you can now use all tsdav methods without supplying authentication header or accounts.

However, you can always pass in custom header or account to override the default for each request.

For Google

const client = await createDAVClient({
serverUrl: 'https://apidata.googleusercontent.com/caldav/v2/',
credentials: {
tokenUrl: 'https://accounts.google.com/o/oauth2/token',
username: 'YOUR_EMAIL_ADDRESS',
refreshToken: 'YOUR_REFRESH_TOKEN_WITH_CALDAV_PERMISSION',
clientId: 'YOUR_CLIENT_ID',
clientSecret: 'YOUR_CLIENT_SECRET',
},
authMethod: 'Oauth',
defaultAccountType: 'caldav',
});

or

const client = await createDAVClient({
serverUrl: 'https://apidata.googleusercontent.com/caldav/v2/',
credentials: {
authorizationCode: 'AUTH_CODE_OBTAINED_FROM_OAUTH_CALLBACK',
tokenUrl: 'https://accounts.google.com/o/oauth2/token',
clientId: 'YOUR_CLIENT_ID',
clientSecret: 'YOUR_CLIENT_SECRET',
},
authMethod: 'Oauth',
defaultAccountType: 'caldav',
});

For Apple

const client = await createDAVClient({
serverUrl: 'https://caldav.icloud.com',
credentials: {
username: 'YOUR_APPLE_ID',
password: 'YOUR_APP_SPECIFIC_PASSWORD',
},
authMethod: 'Basic',
defaultAccountType: 'caldav',
});

Need help generating app-specific passwords? See the Apple app-specific password guide.

After v1.1.0, you have a new way of creating clients.

info

You need to call client.login() with this method before using the functions

For Google

const client = new DAVClient({
serverUrl: 'https://apidata.googleusercontent.com/caldav/v2/',
credentials: {
tokenUrl: 'https://accounts.google.com/o/oauth2/token',
username: 'YOUR_EMAIL_ADDRESS',
refreshToken: 'YOUR_REFRESH_TOKEN_WITH_CALDAV_PERMISSION',
clientId: 'YOUR_CLIENT_ID',
clientSecret: 'YOUR_CLIENT_SECRET',
},
authMethod: 'Oauth',
defaultAccountType: 'caldav',
});

For Apple

const client = new DAVClient({
serverUrl: 'https://caldav.icloud.com',
credentials: {
username: 'YOUR_APPLE_ID',
password: 'YOUR_APP_SPECIFIC_PASSWORD',
},
authMethod: 'Basic',
defaultAccountType: 'caldav',
});

Get calendars

If you are using the class-based DAVClient, call await client.login() once before fetching calendars or other resources.

const calendars = await client.fetchCalendars();

Get calendar objects on calendars

const calendarObjects = await client.fetchCalendarObjects({
calendar: myCalendar,
});

Get specific calendar objects on calendar using urls

const calendarObjects = await client.fetchCalendarObjects({
calendar: myCalendar,
calendarObjectUrls: urlArray,
});
Get calendars changes from remote
const { created, updated, deleted } = await client.syncCalendars({
calendars: myCalendars,
detailedResult: true,
});

Get calendar object changes on a calendar from remote

const { created, updated, deleted } = (
await client.smartCollectionSync({
collection: {
url: localCalendar.url,
ctag: localCalendar.ctag,
syncToken: localCalendar.syncToken,
objects: localCalendarObjects,
objectMultiGet: client.calendarMultiGet,
},
method: 'webdav',
detailedResult: true,
})
).objects;