pixeldrive

SDK

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.

InstallUploadReplaceReadErrorsnpm

Install

Create a project API key in settings. Write can upload; Read can list and mint URLs. One key is bound to one project.

npm
npm install @pixeldrive-dev/sdk
constructor
new Pixeldrive({ apiKey, baseUrl?: "https://api.pixeldrive.dev" })

Default host is https://api.pixeldrive.dev. Pass baseUrl for another host (tests, a preview).

Upload

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.

upload
import { 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);
FieldRequiredNotes
fileyesUint8Array, ArrayBuffer, Blob, or a stream (stream() → ReadableStream)
nameyesPath find-or-creates folders: selects/hero.jpg → folder selects, file hero.jpg
contentTypeyesMIME. Unknown types are rejected.
sizestreamsRequired when file is a stream. Ignored for Blob / typed arrays.
tagsnoApplied after complete
foldernoExisting folder publicId; path in name is relative to it
idempotencyKeyno24 hours, same body
upsertnoIf 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 and delete

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.

replace
const next = await pd.replace(file.publicId, {
  file: await readFile("./hero-v2.jpg"),
  name: "hero-v2.jpg",
  contentType: "image/jpeg",
});

Read

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.

read
const 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");
MethodNeedsReturns
upload()writeFile JSON (url, stableUrl, publicId, …)
replace(publicId, input)writeSame as upload, existing publicId
files.delete(publicId)write204. Purges versions too
projects.get()readThe key’s project listing
folders.get(publicId)readOne folder listing
files.get(publicId, { ttl })readOne file. Private: tokened url + expiresAt
files.getByPath(path, { ttl, folder })readOne file by path (selects/hero.jpg)

Lower-level upload

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.

Errors

Failed requests throw PixeldriveError with status and optional code from the API body. Rename, move, and tags stay on the dashboard.

PixeldriveError
import { Pixeldrive, PixeldriveError } from "@pixeldrive-dev/sdk";

try {
  await pd.upload({ /* … */ });
} catch (error) {
  if (error instanceof PixeldriveError) {
    console.error(error.status, error.code, error.message);
  }
}