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

Filesystem

Local filesystem - the dev/test adapter. Uses node:fs/promises with a sidecar .meta.json per file. Not for production.

Installation

This adapter has no extra peer dependencies - the runtime (Node or Bun) provides everything it needs.

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

Usage

Local filesystem. The dev/test adapter - point it at a directory and it implements the same Adapter contract as the cloud adapters using node:fs/promises. Each upload writes the body and a sidecar .meta.json file alongside it (Content-Type, ETag, user metadata) so reads round-trip cleanly. Not for production: there’s no replication, no signing, no auth.

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

// Writes objects under `./.uploads` with a sidecar `.meta.json`
// per file for Content-Type, ETag, and user metadata. Designed for
// dev and CI - same Adapter contract as the cloud adapters, so swap
// it in via env without changing call sites.
const files = new Files({
  adapter: fs({
    root: "./.uploads",
    // Optional: configure if a dev server exposes the same root over
    // HTTP, so url() returns a browser-friendly URL instead of file://.
    // urlBaseUrl: "http://localhost:3000/files",
  }),
});

Options

Could not generate a type table for FsAdapterOptions: Cannot read properties of undefined (reading 'ESNext')

Storage layout

Body at `${root}/${key}`; sidecar at `${root}/${key}.meta.json`. Sidecars survive cp -r / git mv / partial-tree deletion. list() hides them. ETag is a SHA-1-derived stable hash computed at upload time. Files written into root by hand without a sidecar are still readable - contentType falls back to application/octet-stream and etag is absent.

The separate body and sidecar layout cannot provide a cross-process atomic ETag compare-and-set boundary. The adapter therefore advertises no conditional operations and rejects them before filesystem I/O; it does not emulate CAS with a read followed by a rename.

Compatibility

Method Status Notes
upload
download
delete
list
search
head
exists
copy
url ⚠️ Returns a file:// URL by default - fine for CLIs and tests, not browsers. With urlBaseUrl set, returns <urlBaseUrl>/<key> so a dev server (Next.js /public mount, serve-static, etc.) can deliver the body. responseContentDisposition throws because neither file:// nor static-server URLs have a signature mechanism in which to bind the override.
signedUploadUrl Throws - the fs adapter has no built-in upload server, signer, or verifier, so it cannot bind expiry, content type, or size limits into an upload capability. Upload through files.upload() or an application route that enforces those controls server-side.
conditional operations Body and metadata are separate files, so there is no honest cross-process native CAS primitive.

Was this page helpful?