@qezor/objsys
v1.0.4
Published
fs-like async object storage helpers for S3-compatible APIs such as S3 and R2.
Maintainers
Readme
@qezor/objsys
@qezor/objsys is a generic async object-storage toolkit that feels familiar to fs/promises, but is built for S3-compatible APIs such as:
- Amazon S3
- Cloudflare R2
- MinIO
- any other S3-compatible endpoint
It gives you fs-like async methods without pretending object storage is a real POSIX filesystem.
Install
npm install @qezor/objsysImportant Note
This will not work the way people often assume:
const fs = require("fs") || require("@qezor/objsys")require("fs") always resolves in Node, so the fallback never runs.
Use one of these instead:
const fs = require("@qezor/objsys")or
const fs = process.env.USE_OBJECT_STORAGE === "true"
? require("@qezor/objsys")
: require("fs").promisesEnvironment-backed Usage
export OBJSYS_BUCKET=my-bucket
export OBJSYS_ENDPOINT=https://<account>.r2.cloudflarestorage.com
export OBJSYS_ACCESS_KEY_ID=...
export OBJSYS_SECRET_ACCESS_KEY=...
export OBJSYS_REGION=autoconst fs = require("@qezor/objsys")
await fs.writeFile("notes/hello.txt", "hello world", {
contentType: "text/plain; charset=utf-8",
})
const text = await fs.readFile("notes/hello.txt", "utf8")
const items = await fs.readdir("notes")Explicit Client Usage
const { createObjectSystem } = require("@qezor/objsys")
const fs = createObjectSystem({
bucket: "my-bucket",
endpoint: "https://<account>.r2.cloudflarestorage.com",
accessKeyId: process.env.R2_ACCESS_KEY_ID,
secretAccessKey: process.env.R2_SECRET_ACCESS_KEY,
region: "auto",
prefix: "app-data",
})Core Async Methods
readFile(path, options)createReadStream(path, options)writeFile(path, data, options)readJson(path, options)writeJson(path, value, options)stat(path, options)exists(path, options)access(path, options)readdir(path, options)list(path, options)listPages(path, options)unlink(path, options)rm(path, options)mkdir(path, options)copyFile(source, destination, options)rename(source, destination, options)presignRead(path, options)presignWrite(path, options)
Low-Memory Patterns
For small and medium objects:
const text = await fs.readFile("notes/hello.txt", {
encoding: "utf8",
maxBytes: 1024 * 1024,
})For larger objects or scarce-resource runtimes:
const stream = await fs.createReadStream("exports/big-report.json")
stream.pipe(process.stdout)For precise ranged reads:
const tail = await fs.readFile("archives/bundle.zip", {
startByte: 4096,
endByte: 8192,
})Notes
readdir()andstat()understand pseudo-directories through prefixes.list()is the paginated primitive, andlistPages()is the low-memory async iterator for large prefixes.readFile()supports{ maxBytes }so buffered reads can fail fast instead of growing unbounded in memory.readFile()andcreateReadStream()support{ startByte, endByte }for classic byte-range reads.supportsRanges === truetells higher-level tools such as archive readers that partial object reads are available.createReadStream()is the safer choice for large objects or scarce-resource runtimes.writeFile()accepts strings, buffers, typed arrays, readable streams, and async iterables.unlink()andrm()support{ force: true }for missing objects.copyFile()andrename()can target different buckets/prefixes throughsourceBucket,sourcePrefix,destinationBucket, anddestinationPrefix.mkdir()is a no-op by default unless you pass{ marker: true }.rm(path, { recursive: true })refuses to delete the root prefix unless you pass{ allowRoot: true }.rm(path, { recursive: true })now deletes page-by-page instead of collecting the whole tree in memory first.- Directory markers are hidden from normal listings so the API behaves more like a filesystem.
- There is intentionally no fake
appendFile()helper here, because object storage cannot do safe native appends. promisespoints back to the same async API so the package feels familiar tofs.promisesusers.
