---
title: OneDrive
description: OneDrive and SharePoint document libraries via Microsoft Graph. Path-addressable with no virtual-key bookkeeping, chunked upload sessions past 250 MB.
---

## Installation

`@azure/identity` and `@microsoft/microsoft-graph-client` are optional peer dependencies of `files-sdk` - install alongside the SDK so the adapter's imports resolve at runtime.

```package-install
files-sdk @azure/identity @microsoft/microsoft-graph-client
```

## Usage

OneDrive and SharePoint document libraries via the official `@microsoft/microsoft-graph-client` SDK. Microsoft Graph is path-addressable (`/drive/root:/folder/file.txt`), so the adapter maps virtual keys onto real OneDrive paths - no virtual-key cache, no `fsdkKey` bookkeeping. Five auth shapes (app-only, OAuth refresh token, raw access token, pre-built Graph client, or env-var fallback) and four drive targets (`/me/drive`, `driveId`, `siteId`, `userId`) cover the personal-OneDrive, OneDrive-for-Business, and SharePoint-site-library cases.

```ts lineNumbers
import { Files } from "files-sdk";
import { onedrive } from "files-sdk/onedrive";

// App-only auth (client credentials) into a SharePoint site library.
// Cannot use /me/drive - pass driveId, siteId, or userId instead.
const files = new Files({
  adapter: onedrive({
    clientCredentials: {
      tenantId: process.env.ONEDRIVE_TENANT_ID!,
      clientId: process.env.ONEDRIVE_CLIENT_ID!,
      clientSecret: process.env.ONEDRIVE_CLIENT_SECRET!,
    },
    siteId: process.env.ONEDRIVE_SITE_ID!,
    rootFolderPath: "Uploads",
    // publicByDefault: true → upload() also creates an anonymous-view
    //                       sharing link and url() returns its webUrl.
  }),
});
```

## Options

<AutoTypeTable
  path="../../packages/files-sdk/src/onedrive/index.ts"
  name="OneDriveAdapterOptions"
/>

## Compatibility

| Method | Status | Notes |
| --- | :-: | --- |
| `upload` | ⚠️ | Single-PUT simple upload up to OneDrive's 250 MB limit; larger bodies (or any `multipart` upload) automatically switch to a chunked Graph upload session. `multipart.partSize` tunes the chunk size (rounded to a 320 KiB multiple). Stream bodies are buffered into memory up-front. User `metadata` and `cacheControl` throw - Graph drive items have no native arbitrary-metadata field; use `raw` to set Open Extensions if you need them. |
| `download` | ✅ |  |
| `delete` | ✅ | A folder key is a no-op, like a missing key. `""`, `"/"`, and keys with a trailing slash throw `Invalid` (they name the root or a folder), here and on every other method. |
| `list` | ⚠️ | Lists one folder, no recursion: the folder the `prefix` points into (the part up to its last `/`, under `rootFolderPath` - `photos/` lists `photos`), with the rest of the prefix matched client-side against child names within the page. Keys come back in full (`photos/cover.jpg`). Without a delimiter, subfolders are skipped; with `delimiter: '/'` they come back as `prefixes` (`photos/2024/`) that you can pass back as the next `prefix`. A prefix into a missing folder lists nothing. Pagination uses Graph's `@odata.nextLink` as the opaque cursor. |
| `search` | ⚠️ | Built on `listAll` — inherits this adapter's `list` behavior above. Client-side key match (glob, regex, substring, exact). |
| `head` | ⚠️ | A key that resolves to a folder throws `NotFound` - keys name files. |
| `exists` | ⚠️ | Returns `false` for a key that resolves to a folder. |
| `copy` | ⚠️ | Async copy on Graph (`POST /items/{id}/copy` returns 202 + monitor URL). The adapter polls the monitor every 500 ms until status is `completed`/`failed`, capped by `copyTimeoutMs` (default 60_000). On timeout the call throws a `permanent` `Provider` error ("did not finish within…"), so a retry doesn't start a second copy; the copy may still finish server-side, so check the destination, and tune `copyTimeoutMs` for large files. An existing destination file is overwritten (`conflictBehavior=replace`, falling back to delete and copy); a folder at the destination, or the same file under different casing, is `Conflict`, and a folder as the source is `NotFound`. A destination folder that doesn't exist yet is created first, as `upload` does. `move` behaves the same. |
| `url` | ⚠️ | Throws by default - Graph has no signed URL primitive. With `publicByDefault: true` at construction, `upload()` calls `createLink` (anonymous-view scope) and `url()` returns the share link's `webUrl`. The link is permanent: `url(key)` returns the permanent link; an explicit `expiresIn` throws `Unsupported`. `responseContentDisposition` always throws - Graph has no Content-Disposition override. Anonymous links are blocked on tenants where admins disable them. |
| `signedUploadUrl` | ⚠️ | Initiates a Graph upload session via `POST /createUploadSession` and returns the session URL as a one-shot PUT (the session URL is pre-authenticated by Graph itself). It's an upload session, not a plain presigned PUT: the client must send `Content-Range: bytes 0-{n-1}/{n}` with the body, and a single request must stay under 60 MiB - larger files need several PUTs of 320 KiB-multiple fragments against the same URL. `maxSize` and `minSize` throw because Graph does not enforce a server-side `content-length-range` policy on upload sessions, and `contentType` throws because a session doesn't bind a Content-Type (Graph infers the type from the file name); enforce these at your application gateway instead. |
