@oneminutecloud/storage-bucket
v1.0.0
Published
Backend SDK for OneMinute Cloud Storage Bucket
Readme
@oneminutecloud/storage-bucket
Official backend SDK for OneMinute Cloud Storage Buckets.
Upload, confirm, and inspect files from any Node.js / JavaScript server-side environment — NestJS, Express, Fastify, Next.js API routes, and more.
Table of Contents
- Features
- Requirements
- Installation
- Quick Start
- API Reference
- TypeScript Types
- Framework Guides
- Error Handling
- FAQ
Features
- 🚀 Multipart-aware — handles chunked uploads transparently via a two-step initiate → confirm flow
- 🔒 Secure by default — your API key is only used server-side; never exposed to the browser
- 🌐 Framework-agnostic — works with any Node.js ≥ 18 server framework
- 📦 Zero runtime dependencies — uses the native
fetchAPI (Node 18+) - 🏷️ Fully typed — complete TypeScript definitions included (ESM + CJS dual build)
Requirements
| Requirement | Version | |---|---| | Node.js | ≥ 18.0.0 | | TypeScript (optional) | ≥ 5.0 |
Note: This is a server-side SDK. Never import or use it in client-side/browser bundles — your API key would be exposed.
Installation
# npm
npm install @oneminutecloud/storage-bucket
# yarn
yarn add @oneminutecloud/storage-bucket
# pnpm
pnpm add @oneminutecloud/storage-bucketQuick Start
import { createStorageBucket } from "@oneminutecloud/storage-bucket";
import { readFileSync } from "fs";
// 1. Create a client instance
const bucket = createStorageBucket({
apiKey: "OMC_your_api_key_here",
});
// 2. Read the file into a Buffer
const fileBuffer = readFileSync("./my-video.mp4");
// 3. Initiate the upload
const upload = await bucket.initiateUpload(fileBuffer, {
bucketId: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
duration: 120, // optional: video duration in seconds
});
// 4. Confirm (complete) the upload
const result = await bucket.confirmUpload({
bucketId: upload.bucketId,
objectId: upload.objectId,
uploadId: upload.uploadId,
key: upload.key,
parts: upload.parts,
});
console.log("File stored at key:", result.key);
// 5. Retrieve file metadata any time
const meta = await bucket.getMeta(result.key);
console.log(meta);API Reference
createStorageBucket
Factory function that returns a StorageBucket client instance.
const bucket = createStorageBucket(options: StorageBucketOptions): StorageBucket| Option | Type | Required | Description |
|---|---|---|---|
| apiKey | string | ✅ | Your OneMinute Cloud API key. Must start with OMC_. |
Throws if apiKey is missing or has an invalid format.
initiateUpload
Registers a new object in the bucket and returns the multipart upload credentials.
const result = await bucket.initiateUpload(
file: Buffer | Uint8Array,
options: UploadFileOptions
): Promise<UploadFileResult>Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| file | Buffer \| Uint8Array | ✅ | Raw file data. |
| options.bucketId | string (UUID) | ✅ | Target bucket ID from your OneMinute Cloud dashboard. |
| options.duration | number | ❌ | Media duration in seconds (e.g. for video/audio). Defaults to 0. |
Returns — UploadFileResult
| Field | Type | Description |
|---|---|---|
| objectId | string | Unique ID of the newly created object. |
| key | string | Storage key for the file. |
| uploadId | string | Multipart upload session ID. |
| bucketId | string | The bucket the object belongs to. |
| parts | UploadFilePart[] | Array of parts needed to confirm the upload. |
confirmUpload
Finalises the multipart upload session. Must be called after initiateUpload to make the file permanently accessible.
const result = await bucket.confirmUpload(
options: ConfirmFileOptions
): Promise<ConfirmFileResult>Parameters — ConfirmFileOptions
| Field | Type | Required | Description |
|---|---|---|---|
| bucketId | string | ✅ | Same bucket ID used in initiateUpload. |
| objectId | string | ✅ | Returned by initiateUpload. |
| uploadId | string | ✅ | Returned by initiateUpload. |
| key | string | ✅ | Returned by initiateUpload. |
| parts | UploadFilePart[] | ✅ | Array of { PartNumber, ETag } returned by initiateUpload. |
Returns — ConfirmFileResult
| Field | Type | Description |
|---|---|---|
| key | string | The final storage key of the confirmed file. |
getMeta
Fetches the stored metadata for an existing object by its key.
const meta = await bucket.getMeta(key: string): Promise<FileMeta>Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| key | string | ✅ | The file's storage key (returned from confirmUpload). |
Returns — FileMeta
| Field | Type | Description |
|---|---|---|
| key | string | Storage key. |
| filename | string | Original filename. |
| contentType | string | MIME type (e.g. video/mp4). |
| size | number | File size in bytes. |
| duration | number? | Media duration in seconds (if applicable). |
| createdAt | string | ISO 8601 creation timestamp. |
| updatedAt | string | ISO 8601 last-updated timestamp. |
TypeScript Types
All types are exported from the package root:
import type {
StorageBucketOptions,
UploadFileOptions,
UploadFileResult,
UploadFilePart,
ConfirmFileOptions,
ConfirmFileResult,
FileMeta,
} from "@oneminutecloud/storage-bucket";Framework Guides
Express.js
A complete file-upload endpoint using multer for multipart/form-data parsing:
npm install express multer
npm install --save-dev @types/express @types/multer// src/routes/upload.ts
import express from "express";
import multer from "multer";
import { createStorageBucket } from "@oneminutecloud/storage-bucket";
const router = express.Router();
const upload = multer({ storage: multer.memoryStorage() });
const bucket = createStorageBucket({
apiKey: process.env.OMC_API_KEY!, // store in .env, never hard-code
});
router.post("/upload", upload.single("file"), async (req, res) => {
try {
if (!req.file) {
return res.status(400).json({ error: "No file provided" });
}
// Step 1 — initiate
const initiated = await bucket.initiateUpload(req.file.buffer, {
bucketId: process.env.OMC_BUCKET_ID!,
duration: Number(req.body.duration) || 0,
});
// Step 2 — confirm
const confirmed = await bucket.confirmUpload({
bucketId: initiated.bucketId,
objectId: initiated.objectId,
uploadId: initiated.uploadId,
key: initiated.key,
parts: initiated.parts,
});
return res.status(201).json({ key: confirmed.key });
} catch (err: any) {
return res.status(500).json({ error: err.message });
}
});
export default router;// src/app.ts
import express from "express";
import uploadRouter from "./routes/upload";
const app = express();
app.use(express.json());
app.use("/api", uploadRouter);
app.listen(3000, () => console.log("Server running on http://localhost:3000"));.env
OMC_API_KEY=OMC_your_api_key_here
OMC_BUCKET_ID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxxNestJS
1. Create a reusable StorageBucketModule
npm install @oneminutecloud/storage-bucket// src/storage-bucket/storage-bucket.service.ts
import { Injectable, OnModuleInit } from "@nestjs/common";
import { ConfigService } from "@nestjs/config";
import {
createStorageBucket,
UploadFileOptions,
ConfirmFileOptions,
FileMeta,
} from "@oneminutecloud/storage-bucket";
@Injectable()
export class StorageBucketService implements OnModuleInit {
private bucket: ReturnType<typeof createStorageBucket>;
constructor(private readonly config: ConfigService) {}
onModuleInit() {
this.bucket = createStorageBucket({
apiKey: this.config.getOrThrow<string>("OMC_API_KEY"),
});
}
async upload(file: Buffer, options: UploadFileOptions) {
const initiated = await this.bucket.initiateUpload(file, options);
return this.bucket.confirmUpload({
bucketId: initiated.bucketId,
objectId: initiated.objectId,
uploadId: initiated.uploadId,
key: initiated.key,
parts: initiated.parts,
});
}
async getMeta(key: string): Promise<FileMeta> {
return this.bucket.getMeta(key);
}
}// src/storage-bucket/storage-bucket.module.ts
import { Module } from "@nestjs/common";
import { StorageBucketService } from "./storage-bucket.service";
@Module({
providers: [StorageBucketService],
exports: [StorageBucketService],
})
export class StorageBucketModule {}2. Use it in a feature module
// src/media/media.controller.ts
import {
Controller, Post, UploadedFile,
UseInterceptors, Body, Get, Param,
} from "@nestjs/common";
import { FileInterceptor } from "@nestjs/platform-express";
import { StorageBucketService } from "../storage-bucket/storage-bucket.service";
@Controller("media")
export class MediaController {
constructor(private readonly storageBucket: StorageBucketService) {}
@Post("upload")
@UseInterceptors(FileInterceptor("file"))
async upload(
@UploadedFile() file: Express.Multer.File,
@Body("bucketId") bucketId: string,
@Body("duration") duration?: string,
) {
const result = await this.storageBucket.upload(file.buffer, {
bucketId,
duration: duration ? Number(duration) : 0,
});
return { key: result.key };
}
@Get("meta/:key")
async getMeta(@Param("key") key: string) {
return this.storageBucket.getMeta(key);
}
}// src/media/media.module.ts
import { Module } from "@nestjs/common";
import { MediaController } from "./media.controller";
import { StorageBucketModule } from "../storage-bucket/storage-bucket.module";
@Module({
imports: [StorageBucketModule],
controllers: [MediaController],
})
export class MediaModule {}.env
OMC_API_KEY=OMC_your_api_key_here
OMC_BUCKET_ID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxxTip: Register
ConfigModule.forRoot()in yourAppModuleand@nestjs/configto handle environment variables cleanly.
Next.js (App Router)
Create a server-side Route Handler to keep your API key off the client:
// app/api/upload/route.ts
import { NextRequest, NextResponse } from "next/server";
import { createStorageBucket } from "@oneminutecloud/storage-bucket";
const bucket = createStorageBucket({
apiKey: process.env.OMC_API_KEY!,
});
export async function POST(req: NextRequest) {
try {
const formData = await req.formData();
const file = formData.get("file") as File | null;
const bucketId = formData.get("bucketId") as string;
if (!file) {
return NextResponse.json({ error: "No file provided" }, { status: 400 });
}
const arrayBuffer = await file.arrayBuffer();
const buffer = Buffer.from(arrayBuffer);
// Step 1 — initiate
const initiated = await bucket.initiateUpload(buffer, {
bucketId,
duration: Number(formData.get("duration")) || 0,
});
// Step 2 — confirm
const confirmed = await bucket.confirmUpload({
bucketId: initiated.bucketId,
objectId: initiated.objectId,
uploadId: initiated.uploadId,
key: initiated.key,
parts: initiated.parts,
});
return NextResponse.json({ key: confirmed.key }, { status: 201 });
} catch (err: any) {
return NextResponse.json({ error: err.message }, { status: 500 });
}
}// app/api/meta/[key]/route.ts
import { NextRequest, NextResponse } from "next/server";
import { createStorageBucket } from "@oneminutecloud/storage-bucket";
const bucket = createStorageBucket({ apiKey: process.env.OMC_API_KEY! });
export async function GET(
_req: NextRequest,
{ params }: { params: { key: string } },
) {
try {
const meta = await bucket.getMeta(params.key);
return NextResponse.json(meta);
} catch (err: any) {
return NextResponse.json({ error: err.message }, { status: 500 });
}
}.env.local
OMC_API_KEY=OMC_your_api_key_hereImportant: Never use this SDK inside a
"use client"component or any browser-side code. Use it only in Route Handlers (route.ts), Server Actions, orgetServerSideProps.
Fastify
npm install fastify @fastify/multipart// src/server.ts
import Fastify from "fastify";
import multipart from "@fastify/multipart";
import { createStorageBucket } from "@oneminutecloud/storage-bucket";
const app = Fastify({ logger: true });
app.register(multipart);
const bucket = createStorageBucket({
apiKey: process.env.OMC_API_KEY!,
});
app.post("/upload", async (request, reply) => {
const data = await request.file();
if (!data) {
return reply.status(400).send({ error: "No file provided" });
}
const chunks: Buffer[] = [];
for await (const chunk of data.file) {
chunks.push(chunk);
}
const fileBuffer = Buffer.concat(chunks);
// Initiate
const initiated = await bucket.initiateUpload(fileBuffer, {
bucketId: process.env.OMC_BUCKET_ID!,
duration: 0,
});
// Confirm
const confirmed = await bucket.confirmUpload({
bucketId: initiated.bucketId,
objectId: initiated.objectId,
uploadId: initiated.uploadId,
key: initiated.key,
parts: initiated.parts,
});
return reply.status(201).send({ key: confirmed.key });
});
app.get<{ Params: { key: string } }>("/meta/:key", async (request, reply) => {
const meta = await bucket.getMeta(request.params.key);
return reply.send(meta);
});
app.listen({ port: 3000 }, () => {
console.log("Server running on http://localhost:3000");
});Error Handling
All SDK methods throw a plain Error with a descriptive message prefixed with [OneMinute Cloud].
try {
const initiated = await bucket.initiateUpload(fileBuffer, { bucketId });
const confirmed = await bucket.confirmUpload({ ...initiated, parts: initiated.parts });
console.log("Uploaded:", confirmed.key);
} catch (err) {
if (err instanceof Error) {
// e.g. "[OneMinute Cloud] Invalid bucket ID"
// e.g. "[OneMinute Cloud] Upload initiation failed"
console.error(err.message);
}
}Common Error Messages
| Error | Cause |
|---|---|
| apiKey is required | No API key was passed to createStorageBucket. |
| Invalid API key format | API key does not start with OMC_. |
| bucketId is required | bucketId is missing from the options. |
| Invalid bucket ID | bucketId is not a valid UUID. |
| File data is required | An empty Buffer was passed to initiateUpload. |
| Upload initiation failed | The API returned a non-2xx response during initiation. |
| Upload confirmation failed | The API returned a non-2xx response during confirmation. |
| Failed to get file metadata | The API returned a non-2xx response for getMeta. |
FAQ
Q: Do I need to split the file into parts manually?
A: No. initiateUpload handles this automatically. Just pass the raw Buffer and call confirmUpload with the result.
Q: Can I use this in the browser or in a React component?
A: No. This is a server-side-only SDK. Your API key must never be exposed to the client. Use a backend route (Express, NestJS, Next.js Route Handler, etc.) to proxy uploads.
Q: What file types are supported?
A: Any file type. The SDK sends application/octet-stream as the content type for raw Buffers. If you pass a browser File object (e.g. in a Bun/Deno environment), the file's native MIME type is used automatically.
Q: Where do I find my Bucket ID and API Key?
A: Log in to your OneMinute Cloud Dashboard, navigate to Storage Buckets, and copy the bucket UUID. Your API key is available in Settings → Developer Access.
Q: Is there a file size limit?
A: Limits depend on your OneMinute Cloud plan. The SDK itself imposes no limit — it streams multipart metadata to the API. Check your plan's quota in the dashboard.
Contributing & Support
- Issues: GitHub Issues
- Homepage: OneMinute Cloud
License
ISC © OneMinute Stack
