Skip to content
Files SDK
Esc
navigateopen⌘Jpreview
On this page

WebDAV

WebDAV (Nextcloud, ownCloud, Apache mod_dav, …) via the webdav client. HTTP-based, with native server-side copy/move; url() needs an HTTP front.

Installation

webdav is an optional peer dependency of files-sdk - install alongside the SDK so the adapter’s imports resolve at runtime.

npm install files-sdk webdav
pnpm add files-sdk webdav
yarn add files-sdk webdav
bun add files-sdk webdav

Usage

WebDAV via the webdav library. Virtual keys map to paths under a configurable root on the server, with a .. traversal guard. Because WebDAV is plain HTTP (PROPFIND / GET / PUT / COPY / MOVE / DELETE), the adapter is transport-agnostic and works against Nextcloud, ownCloud, Apache mod_dav, sabre/dav, box.com, and most NAS boxes.

import { Files } from "files-sdk";
import { webdav } from "files-sdk/webdav";

const files = new Files({
  adapter: webdav({
    baseUrl: "https://cloud.example.com/remote.php/dav/files/alice",
    username: process.env.WEBDAV_USERNAME!,
    password: process.env.WEBDAV_PASSWORD!,
    // authType: "digest", // "basic" (default) | "digest" | "token" | "none"
    root: "/uploads", // virtual keys resolve under here; defaults to "/"
  }),
});

await files.upload("reports/q1.csv", csv, { contentType: "text/csv" });
const file = await files.download("reports/q1.csv");

Config falls back to WEBDAV_URL (alias WEBDAV_BASE_URL), WEBDAV_USERNAME (alias WEBDAV_USER), WEBDAV_PASSWORD, and WEBDAV_AUTH_TYPE when the matching option is omitted. For OAuth, pass authType: "token" with a token object.

Reusing a client

The webdav client is stateless - it holds config and issues a fresh HTTP request per call - so there’s no connection to pool. Pass a pre-configured client when you want to control its options (custom headers, a shared instance) directly:

import { createClient } from "webdav";

const client = createClient(
  "https://cloud.example.com/remote.php/dav/files/alice",
  {
    username,
    password,
  }
);

const files = new Files({ adapter: webdav({ client }) });

Options

PropType
baseUrl?string

WebDAV server base URL — the collection virtual keys resolve under (e.g. `https://dav.example.com/remote.php/dav/files/alice`). Falls back to `WEBDAV_URL` (alias `WEBDAV_BASE_URL`).

Typestring
username?string

Username for basic/digest auth. Falls back to `WEBDAV_USERNAME` (alias `WEBDAV_USER`).

Typestring
password?string

Password for basic/digest auth. Falls back to `WEBDAV_PASSWORD`.

Typestring
authType?WebdavAuthType

Auth strategy. Inferred when omitted (see {@link WebdavAuthType}). Falls back to `WEBDAV_AUTH_TYPE`.

TypeWebdavAuthType
token?OAuthToken

OAuth token for `authType: "token"`.

TypeOAuthToken
headers?Record<string, string>

Extra headers sent on every request (e.g. a custom auth header).

TypeRecord<string, string>
root?string

Remote base directory. Virtual keys resolve under it; keys that escape it (e.g. `../secret`) throw `Provider`. Defaults to `"/"` (the collection the `baseUrl` points at).

Typestring
publicBaseUrl?string

Origin used to build URLs from `url()`. When set, `url(key)` returns `${publicBaseUrl}/${key}`. When unset, `url()` throws: a WebDAV `GET` needs auth, so there's no unauthenticated URL to hand out, and the protocol has no signing primitive.

Typestring
client?WebDAVClient

Pre-configured `webdav` client. When passed, the adapter reuses it and ignores the connection options above — the caller owns its configuration.

TypeWebDAVClient

Limitations

list walks the collection tree with one PROPFIND per directory rather than a single Depth: infinity request - many servers disable infinite-depth listing, so the walk is the portable choice, but a large tree means many round-trips per list() call.

There’s no resumable/chunked upload: WebDAV has no portable append primitive, so upload({ control }) is unsupported and unknown-length streams are buffered before the PUT.

Compatibility

Method Status Notes
upload ⚠️ User metadata and cacheControl throw - WebDAV has no arbitrary-metadata or cache-header field. contentType is sent as the PUT Content-Type, so servers that persist it round-trip it on read. Stream bodies are buffered (no chunked upload).
download Ranged reads issue a Range request; as: "stream" streams the response body without buffering. A server that ignores Range throws rather than silently returning the whole object.
delete Idempotent - a missing file is not an error.
list ⚠️ Walks the collection tree recursively (one PROPFIND per directory). prefix/limit/cursor/delimiter are applied client-side over the full walk. size/lastModified/type come from the PROPFIND props.
search ⚠️ Built on listAll — inherits this adapter’s list behavior above. Client-side key match (glob, regex, substring, exact).
head ⚠️ size and lastModified come from PROPFIND; the content type is the server’s getcontenttype prop, falling back to the key’s extension. No etag surfaced.
exists A collection (directory) reports false - exists answers for file keys.
copy Native server-side COPY - no body round-trip through this process.
move Native server-side MOVE.
url Throws unless publicBaseUrl is set (an HTTP server fronting the same tree), in which case it returns <publicBaseUrl>/<key>. A WebDAV GET needs authentication and the protocol has no signing primitive. responseContentDisposition always throws because the HTTP-front URL cannot bind the override.
signedUploadUrl Throws - WebDAV has no presigned-upload concept. Use upload().

Was this page helpful?