TypeScript client @pixeldrive-dev/sdk. It wraps the same HTTP as the API: upload() hides put vs multipart. replace() and files.delete use the same write key. Node 18+ (or any runtime with fetch). Keep the key on the server.
Create a project API key in settings. Write can upload; Read can list and mint URLs. One key is bound to one project.
npmnpm install @pixeldrive-dev/sdkconstructornew Pixeldrive({ apiKey, baseUrl?: "https://api.pixeldrive.dev" })Default host is https://api.pixeldrive.dev. Pass baseUrl for another host (tests, a preview).
upload() starts the session, PUTs the bytes, then completes. You do not choose put vs multipart. Blobs are sliced, not copied into one buffer. Streams need size. On failure it aborts the in-flight upload.
uploadimport { readFile } from "node:fs/promises";
import { Pixeldrive } from "@pixeldrive-dev/sdk";
const pd = new Pixeldrive({ apiKey: process.env.PIXELDRIVE_API_KEY });
const file = await pd.upload({
file: await readFile("./hero.jpg"),
name: "selects/hero.jpg",
contentType: "image/jpeg",
tags: ["favorites"],
});
console.log(file.publicId, file.url);| Field | Required | Notes |
|---|---|---|
| file | yes | Uint8Array, ArrayBuffer, Blob, or a stream (stream() → ReadableStream) |
| name | yes | Path find-or-creates folders: selects/hero.jpg → folder selects, file hero.jpg |
| contentType | yes | MIME. Unknown types are rejected. |
| size | streams | Required when file is a stream. Ignored for Blob / typed arrays. |
| tags | no | Applied after complete |
| folder | no | Existing folder publicId; path in name is relative to it |
| idempotencyKey | no | 24 hours, same body |
| upsert | no | If a ready file exists at this path, replace it (409 without this) |
Project upload rules apply the same as the dashboard. Caps and rate limits are on the API page.
replace(publicId, input) is the same PUT loop as upload. Folder and tags stay; the name becomes the new file’s basename. Kind may change if the project allows it. The file must be ready — an in-flight replace is 409. files.delete(publicId) is 204. Cached public URLs may still serve until TTL.
replaceconst next = await pd.replace(file.publicId, {
file: await readFile("./hero-v2.jpg"),
name: "hero-v2.jpg",
contentType: "image/jpeg",
});The key’s project is implicit, so projects.get() needs no id. Pass a folder or file publicId for the others. Private files return a tokened URL; pass ttl (seconds) on files.get.
readconst project = await pd.projects.get();
const folder = await pd.folders.get("folderPublicId");
const one = await pd.files.get("filePublicId"); // { ttl } for private links
await pd.files.getByPath("selects/hero.jpg");
await pd.files.delete("filePublicId");| Method | Needs | Returns |
|---|---|---|
| upload() | write | File JSON (url, stableUrl, publicId, …) |
| replace(publicId, input) | write | Same as upload, existing publicId |
| files.delete(publicId) | write | 204. Purges versions too |
| projects.get() | read | The key’s project listing |
| folders.get(publicId) | read | One folder listing |
| files.get(publicId, { ttl }) | read | One file. Private: tokened url + expiresAt |
| files.getByPath(path, { ttl, folder }) | read | One file by path (selects/hero.jpg) |
If you want progress or your own PUT loop: startUpload → PUT to the returned URLs → completeUpload. abortUpload on failure. Pass replace on start for a replace session. Multipart complete needs part ETags. Same JSON as POST /v1/upload.
Failed requests throw PixeldriveError with status and optional code from the API body. Rename, move, and tags stay on the dashboard.
PixeldriveErrorimport { Pixeldrive, PixeldriveError } from "@pixeldrive-dev/sdk";
try {
await pd.upload({ /* … */ });
} catch (error) {
if (error instanceof PixeldriveError) {
console.error(error.status, error.code, error.message);
}
}