---
title: Appwrite
description: Appwrite Storage via the official node-appwrite SDK. Auto-loads APPWRITE_ENDPOINT, APPWRITE_PROJECT_ID, and APPWRITE_API_KEY, or pass an existing client.
peerDeps:
  - "node-appwrite"
---

## Installation

`node-appwrite` is an optional peer dependency of `files-sdk` - install alongside the SDK so the adapter's imports resolve at runtime.

```package-install
files-sdk node-appwrite
```

## Usage

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

const files = new Files({
  adapter: appwrite({
    bucket: "uploads",
    // Auto-loads from APPWRITE_ENDPOINT, APPWRITE_PROJECT_ID,
    // and APPWRITE_API_KEY. Or pass an existing node-appwrite
    // Client or Storage instance via `client`.
    //
    // Note: Appwrite keys (IDs) must be alphanumeric/dashes
    // and max 36 chars. Slashes (/) are not supported.
  }),
});
```

## Options

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

## Limitations

File IDs (keys) must start with an alphanumeric and use only `[a-zA-Z0-9._-]`, max 36 characters (no slashes) - invalid keys are rejected before the API call. `list({ prefix })` queries `startsWith("$id", ...)` against the canonical file ID, so files created outside the adapter where the display `name` differs from `$id` won't be matched by prefix.

## Compatibility

| Method            | Status | Notes                                                                                                                                                                                                                                                                                                                                                   |
| ----------------- | :----: | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `upload`          |   ⚠️   | Stream bodies are buffered up-front - `InputFile.fromBuffer` has no streaming form, so streamed uploads can't avoid materializing the body in memory. User `metadata` and `cacheControl` throw - Appwrite's `createFile` has no equivalent fields. `contentType` is silently ignored - Appwrite auto-detects mime from the payload and has no override. |
| `download`        |   ✅   |                                                                                                                                                                                                                                                                                                                                                         |
| `delete`          |   ✅   |                                                                                                                                                                                                                                                                                                                                                         |
| `list`            |   ✅   |                                                                                                                                                                                                                                                                                                                                                         |
| `search`          |   ✅   |                                                                                                                                                                                                                                                                                                                                                         |
| `head`            |   ✅   |                                                                                                                                                                                                                                                                                                                                                         |
| `exists`          |   ✅   |                                                                                                                                                                                                                                                                                                                                                         |
| `copy`            |   ⚠️   | Read-then-write - Appwrite has no server-side copy primitive, so the source is downloaded and re-uploaded. Costs an egress + an ingest; not atomic.                                                                                                                                                                                                     |
| `url`             |   ⚠️   | Throws by default because Appwrite SDKs cannot mint presigned reading URLs with keys. Set `public: true` at construction to return the constructed Appwrite public CDN URL. `expiresIn` and `responseContentDisposition` are ignored.                                                                                                                   |
| `signedUploadUrl` |   ❌   | No presigned upload primitive in Appwrite. Use JWTs or client SDKs for direct uploads.                                                                                                                                                                                                                                                                  |
