@smarthivelabs-devs/storage-next
v0.2.1
Published
Next.js server helpers for SmartHive Storage.
Downloads
361
Readme
@smarthivelabs-devs/storage-next
Next.js server-side helpers for SmartHive Storage. Reads credentials from environment variables automatically.
Install
npm install @smarthivelabs-devs/storage-next @smarthivelabs-devs/storage-sdkEnvironment Variables
All three are required. An empty string throws a config error at client creation time — you will not get silent failures.
| Variable | Description |
|----------|-------------|
| SMARTHIVE_STORAGE_URL | Base URL of your SmartHive Storage server |
| SMARTHIVE_STORAGE_APP_CODE | App code from admin console |
| SMARTHIVE_STORAGE_API_KEY | API key — keep this server-only, never expose to clients |
Route Handler — File Upload
// app/api/upload/route.ts
import { createStorageRouteHandlerClient } from '@smarthivelabs-devs/storage-next';
import { NextRequest, NextResponse } from 'next/server';
export async function POST(req: NextRequest) {
const client = createStorageRouteHandlerClient();
const formData = await req.formData();
const file = formData.get('file') as File | null;
if (!file) {
return NextResponse.json({ error: 'No file provided' }, { status: 400 });
}
const result = await client.upload({
bucket: process.env.BUCKET_CODE!,
file,
filename: file.name,
contentType: file.type,
visibility: 'PRIVATE',
});
return NextResponse.json({ fileId: result.fileId, url: result.url });
}Route Handler — File Metadata
// app/api/files/[fileId]/route.ts
import { createStorageRouteHandlerClient } from '@smarthivelabs-devs/storage-next';
export async function GET(
_req: Request,
{ params }: { params: Promise<{ fileId: string }> },
) {
const { fileId } = await params;
const client = createStorageRouteHandlerClient();
const meta = await client.getMetadata(fileId);
return Response.json(meta);
}Private Bucket Delivery
When a bucket is PRIVATE, your Next.js server generates signed URLs on demand. The client browser fetches the signed URL directly — your API key never leaves the server.
Client browser ──POST /api/files/[id]/signed-url──► Next.js route handler
│
▼ client.getSignedUrl()
SmartHive Storage (creates token)
│
◄── { url: "https://storage.…?token=shat_…" }
◄──────────────────────────── { url } ────────────────┘
Client browser ──GET url──► SmartHive Storage (validates token, streams file)
◄─────────────── file bytes ─────────────────────────────────────────────────Route Handler — Generate Signed URL
// app/api/files/[fileId]/signed-url/route.ts
import { createStorageRouteHandlerClient } from '@smarthivelabs-devs/storage-next';
export async function POST(
req: Request,
{ params }: { params: Promise<{ fileId: string }> },
) {
const { fileId } = await params;
// Add your own auth check here — verify the requesting user owns this file.
const client = createStorageRouteHandlerClient();
const signed = await client.getSignedUrl(fileId, {
expiresInSeconds: 300, // 5 minutes
maxUses: 1, // single-use
});
// signed.url is already the full URL with token embedded — return it to the client
return Response.json({ url: signed.url, expiresAt: signed.expiresAt });
}Client component usage
// No auth headers needed — the token is in the URL
function PrivateImage({ fileId }: { fileId: string }) {
const [src, setSrc] = useState<string | null>(null);
useEffect(() => {
fetch(`/api/files/${fileId}/signed-url`, { method: 'POST' })
.then((r) => r.json())
.then((d) => setSrc(d.url));
}, [fileId]);
return src ? <img src={src} alt="" /> : null;
}Server Actions
// app/actions.ts
'use server';
import { createStorageServerActionClient } from '@smarthivelabs-devs/storage-next';
export async function uploadFile(formData: FormData) {
const client = createStorageServerActionClient();
const file = formData.get('file') as File;
return client.upload({
bucket: process.env.BUCKET_CODE!,
file,
filename: file.name,
contentType: file.type,
});
}
export async function getFileUrl(fileId: string) {
const client = createStorageServerActionClient();
return client.getUrl(fileId);
}
export async function deleteFile(fileId: string) {
const client = createStorageServerActionClient();
return client.delete(fileId);
}Server Components — Download Proxy
For private files you want to proxy through your Next.js server (so the API key never hits the browser):
// app/api/files/[fileId]/download/route.ts
import { createStorageRouteHandlerClient } from '@smarthivelabs-devs/storage-next';
export async function GET(
_req: Request,
{ params }: { params: Promise<{ fileId: string }> },
) {
const { fileId } = await params;
const client = createStorageRouteHandlerClient();
// Get a short-lived signed URL and proxy-redirect to it
const signed = await client.getSignedUrl(fileId, { expiresInSeconds: 30, maxUses: 1 });
return Response.redirect(signed.url, 302);
}Custom Config Override
All three factory functions accept optional overrides:
const client = createStorageRouteHandlerClient({
baseUrl: 'https://other-storage.example.com', // override env var
timeoutMs: 15_000,
onResponse: (info) => console.log(`${info.method} ${info.path} → ${info.status} (${info.durationMs}ms)`),
});Error Handling
import { StorageApiError } from '@smarthivelabs-devs/storage-sdk';
try {
await client.getMetadata(fileId);
} catch (err) {
if (err instanceof StorageApiError && err.status === 404) {
return NextResponse.json({ error: 'File not found' }, { status: 404 });
}
throw err;
}