hydrous
v2.0.1
Published
Official JavaScript/TypeScript SDK for the Hydrous platform — auth, records, analytics, and storage
Maintainers
Readme
Hydrous SDK
Official JavaScript / TypeScript SDK for the Hydrous platform.
One package — auth, records, analytics, and storage.
npm install hydrousTable of Contents
1. Quick Start
import { createClient } from 'hydrous';
const hydrous = createClient({
url: 'https://api.yourapp.hydrous.app',
apiKey: 'hk_live_…',
});
// Upload a file
const { data, error } = await hydrous.storage.upload(
'ssk_my_bucket_key', // ← bucket key always comes first
file,
{
path: 'avatars/alice.jpg',
onProgress: (p) => console.log(`${p.percent}%`),
}
);2. Configuration
import { HydrousClient } from 'hydrous';
const hydrous = new HydrousClient({
url: 'https://api.yourapp.hydrous.app', // your project URL
apiKey: 'hk_live_…', // your project API key
timeout: 30_000, // optional — ms (default 30s)
});| Option | Type | Required | Description |
|-----------|----------|----------|--------------------------------------|
| url | string | ✅ | Your Hydrous project base URL |
| apiKey | string | ✅ | Your project API key |
| timeout | number | ✗ | Request timeout in ms (default 30000) |
3. Auth
Sign Up
const { data, error } = await hydrous.auth.signUp({
email: '[email protected]',
password: 'supersecret',
metadata: { plan: 'pro' }, // optional
});
if (data) {
console.log('New user:', data.user.id);
console.log('Access token:', data.accessToken);
}Sign In
const { data, error } = await hydrous.auth.signIn({
email: '[email protected]',
password: 'supersecret',
});
if (data) {
console.log('Welcome back,', data.user.email);
}Sign Out
await hydrous.auth.signOut();Get Current User
const { data: user } = await hydrous.auth.getUser();
console.log(user?.email);Refresh Session
const { data: session } = await hydrous.auth.refreshSession();4. Records
Insert
Single record:
const { data, error } = await hydrous.records.insert('users', {
name: 'Alice',
email: '[email protected]',
role: 'admin',
});Bulk insert (array):
const { data } = await hydrous.records.insert('products', [
{ name: 'Widget A', price: 9.99 },
{ name: 'Widget B', price: 14.99 },
]);
console.log(`Inserted ${data.length} products`);Select / Query
const { data, count } = await hydrous.records.select('users', {
where: { field: 'role', operator: 'eq', value: 'admin' },
orderBy: { field: 'createdAt', direction: 'desc' },
limit: 20,
offset: 0,
select: ['id', 'name', 'email'], // optional column projection
});Multiple filters:
import { eq, gt, inArray } from 'hydrous';
const { data } = await hydrous.records.select('orders', {
where: [
eq('status', 'shipped'),
gt('total', 100),
inArray('tag', ['vip', 'priority']),
],
});Get by ID
const { data: user } = await hydrous.records.get('users', 'user_abc123');Update
const { data } = await hydrous.records.update('users', 'user_abc123', {
name: 'Alice Smith',
});Delete
const { error } = await hydrous.records.delete('users', 'user_abc123');Query Helpers
| Helper | SQL equivalent |
|---------------------|------------------------|
| eq(field, val) | field = val |
| neq(field, val) | field != val |
| gt(field, val) | field > val |
| lt(field, val) | field < val |
| inArray(field, []) | field IN (…) |
import { eq, gt, inArray } from 'hydrous';5. Analytics
Track an Event
await hydrous.analytics.track({
event: 'page_view',
properties: { page: '/home', referrer: 'google.com' },
userId: 'user_abc123',
sessionId: 'sess_xyz',
});Batch Track
More efficient than calling track() in a loop:
await hydrous.analytics.trackBatch([
{ event: 'signup', userId: 'u1' },
{ event: 'onboarded', userId: 'u1', properties: { step: 'profile' } },
]);Query Events
const { data, count } = await hydrous.analytics.query({
event: 'page_view',
from: '2024-01-01',
to: '2024-01-31',
limit: 500,
groupBy: 'properties.page',
});6. Storage
The storage module handles all file operations. Every method takes a bucket key as its first argument — a string that begins with ssk_.
How Bucket Keys Work
A bucket key (ssk_…) is a scoped credential that grants specific permissions (read / write / delete) to a bucket. You create them in the Hydrous dashboard.
hydrous.storage.<method>(
'ssk_your_bucket_key', // ← first, always a string
...args
)Upload a File
const { data, error } = await hydrous.storage.upload(
'ssk_my_bucket_key',
file, // File | Blob | Uint8Array | ArrayBuffer
{
path: 'avatars/alice.jpg', // destination path in your bucket
overwrite: true, // replace if exists (default: false)
onProgress: (progress) => {
console.log(progress.stage, progress.percent + '%');
},
}
);
if (data) {
console.log('Stored at:', data.path);
console.log('Space saved:', data.spaceSaved, 'bytes');
}Upload Raw Text / JSON
No File object needed — pass any string directly.
// Upload a plain text file
await hydrous.storage.uploadText(
'ssk_my_bucket_key',
'reports/summary.txt',
'Monthly report content…',
{ mimeType: 'text/plain' }
);
// Upload JSON
await hydrous.storage.uploadText(
'ssk_my_bucket_key',
'data/config.json',
JSON.stringify({ theme: 'dark', lang: 'en' }),
{ mimeType: 'application/json' }
);Track Upload Progress
onProgress is called on every progress tick with a rich UploadProgress object.
await hydrous.storage.upload(
'ssk_my_bucket_key',
file,
{
onProgress: (p) => {
// p.stage — 'pending' | 'compressing' | 'uploading' | 'done' | 'error'
// p.percent — 0–100 integer
// p.bytesUploaded — bytes sent so far
// p.totalBytes — total bytes to send
// p.bytesPerSecond — current speed (null before first tick)
// p.eta — estimated seconds remaining (null until speed is known)
// p.index — file index (always 0 for single uploads)
// p.total — total files (always 1 for single uploads)
switch (p.stage) {
case 'pending':
console.log('Queued');
break;
case 'compressing':
console.log('Compressing…');
break;
case 'uploading':
console.log(`Uploading ${p.percent}% — ${formatSpeed(p.bytesPerSecond)} — ETA ${p.eta}s`);
break;
case 'done':
console.log('✅ Done!', p.result);
break;
case 'error':
console.error('❌ Error:', p.error);
break;
}
},
}
);
function formatSpeed(bps: number | null): string {
if (!bps) return '—';
if (bps > 1_000_000) return `${(bps / 1_000_000).toFixed(1)} MB/s`;
if (bps > 1_000) return `${(bps / 1_000).toFixed(0)} KB/s`;
return `${bps} B/s`;
}Stage lifecycle:
pending → compressing → uploading → done
↘ errorNote: In browsers, upload progress (bytes leaving the NIC) is tracked via
XMLHttpRequest. The finaldonestage fires only after the server confirms the write to cloud storage — so100%means the file is truly saved.
Batch Upload
Upload many files in one request. Progress fires per-file so you can render individual progress bars.
const inputFiles = Array.from(fileInput.files); // FileList → array
const { data, error } = await hydrous.storage.batchUpload(
'ssk_my_bucket_key',
inputFiles,
{
prefix: 'uploads/2024/', // prepended to each filename
overwrite: false,
concurrency: 5, // max parallel uploads on the server (1–10)
onProgress: (p) => {
// p.index identifies WHICH file this event is for (0-based)
console.log(`File ${p.index} (${p.path}): ${p.stage} ${p.percent}%`);
},
}
);
console.log('Succeeded:', data.succeeded.length);
console.log('Failed:', data.failed.length);Per-file paths override:
await hydrous.storage.batchUpload('ssk_key', files, {
paths: [
'documents/report-q1.pdf',
'documents/report-q2.pdf',
],
});All files are validated upfront before any uploads begin — if your quota would be exceeded the entire batch is rejected cleanly with no partial writes.
Download a File
Returns the file as an ArrayBuffer.
const { data: buffer, error } = await hydrous.storage.download(
'ssk_my_bucket_key',
'avatars/alice.jpg'
);
if (buffer) {
// Browser: display image
const blob = new Blob([buffer], { type: 'image/jpeg' });
img.src = URL.createObjectURL(blob);
// Node: write to disk
import { writeFileSync } from 'fs';
writeFileSync('alice.jpg', Buffer.from(buffer));
}Batch Download
const { data: files } = await hydrous.storage.batchDownload(
'ssk_my_bucket_key',
['reports/jan.pdf', 'reports/feb.pdf', 'reports/mar.pdf'],
{
concurrency: 3,
autoSave: true, // browser: auto-triggers Save dialog per file
onProgress: (p) => {
console.log(`${p.path}: ${p.status}`); // 'pending' | 'success' | 'error'
},
}
);
// files[n].content — ArrayBuffer
// files[n].mimeType — string
// files[n].path — original path
// files[n].size — bytesList Files & Folders
const { data } = await hydrous.storage.list('ssk_my_bucket_key', {
prefix: 'avatars/', // list inside a folder — omit for root
limit: 50, // max items per page (1–100)
});
for (const item of data.items) {
if (item.type === 'folder') {
console.log('📁', item.path);
} else {
console.log('📄', item.path, item.size, 'bytes');
}
}
// Paginate
if (data.pagination.hasNextPage) {
const page2 = await hydrous.storage.list('ssk_my_bucket_key', {
prefix: 'avatars/',
cursor: data.pagination.nextCursor,
});
}File Metadata
const { data: meta } = await hydrous.storage.metadata(
'ssk_my_bucket_key',
'avatars/alice.jpg'
);
console.log(meta.size); // stored bytes
console.log(meta.originalSize); // bytes before compression
console.log(meta.mimeType);
console.log(meta.isCompressed);
console.log(meta.updatedAt);Delete a File
const { error } = await hydrous.storage.deleteFile(
'ssk_my_bucket_key',
'avatars/old-photo.jpg'
);Delete a Folder
Recursively deletes the folder and everything inside it.
await hydrous.storage.deleteFolder('ssk_my_bucket_key', 'temp/');Create a Folder
await hydrous.storage.createFolder('ssk_my_bucket_key', 'avatars/2024/');Move a File
await hydrous.storage.move(
'ssk_my_bucket_key',
'drafts/report.pdf',
'published/report.pdf'
);Copy a File
await hydrous.storage.copy(
'ssk_my_bucket_key',
'templates/invoice.pdf',
'invoices/invoice-001.pdf'
);Signed URLs
Generate a time-limited public URL for a private file.
const { data } = await hydrous.storage.signedUrl(
'ssk_my_bucket_key',
'contracts/agreement.pdf',
{ expiresIn: 300 } // 5 minutes (default: 3600 = 1 hour)
);
console.log(data.signedUrl); // share this — expires at data.expiresAtBucket Stats
const { data: stats } = await hydrous.storage.stats('ssk_my_bucket_key');
console.log('Files:', stats.totalFiles);
console.log('Stored:', stats.totalSizeBytes, 'bytes');
console.log('Space saved:', stats.spaceSavedBytes, 'bytes');
console.log('Credits used:', stats.creditsTotalUsed);
console.log('Compression:', stats.compressionRatio);7. Error Handling
Every method returns { data, error }. error is null on success.
const { data, error } = await hydrous.storage.upload('ssk_key', file);
if (error) {
console.error(error.message); // human-readable message
console.error(error.code); // machine-readable code e.g. 'QUOTA_EXCEEDED'
console.error(error.status); // HTTP status code (if applicable)
}Catching thrown errors
If you prefer try/catch, import isHydrousError:
import { isHydrousError } from 'hydrous';
try {
// ...
} catch (err) {
if (isHydrousError(err)) {
console.error(err.code, err.message);
}
}Common error codes
| Code | Meaning |
|---------------------|----------------------------------------------|
| HTTP_ERROR | Non-2xx HTTP response from the server |
| NETWORK_ERROR | Failed to reach the server |
| QUOTA_EXCEEDED | Storage quota has been reached |
| FILE_TOO_LARGE | File exceeds the per-file size limit (50 MB) |
| INVALID_MIME | MIME type not permitted |
| FILE_EXISTS | File already exists and overwrite is false |
| UPLOAD_ABORTED | Upload was cancelled by the client |
| UPLOAD_TIMEOUT | Upload timed out |
| NO_SESSION | Auth operation requires a session |
| UNKNOWN_ERROR | An unexpected error occurred |
8. TypeScript Types Reference
UploadProgress
interface UploadProgress {
index: number; // file index (0-based)
total: number; // total files in the operation
path: string; // destination path
stage: UploadStage; // see below
bytesUploaded: number;
totalBytes: number;
percent: number; // 0–100
bytesPerSecond: number | null; // null before first tick
eta: number | null; // seconds remaining, null until speed known
result?: UploadResult; // set when stage === 'done'
error?: string; // set when stage === 'error'
code?: string;
}
type UploadStage = 'pending' | 'compressing' | 'uploading' | 'done' | 'error';UploadResult
interface UploadResult {
path: string;
compressed: boolean;
originalSize: number;
storedSize: number;
spaceSaved: number;
mimeType: string;
}StorageItem (from list())
interface StorageItem {
name: string;
path: string;
type: 'file' | 'folder';
size?: number | null;
mimeType?: string | null;
isCompressed?: boolean;
updatedAt?: string | null;
}StorageStats
interface StorageStats {
totalFiles: number;
totalSizeBytes: number;
totalOriginalSizeBytes: number;
spaceSavedBytes: number;
uploadsCount: number;
downloadsCount: number;
creditsUsedUpload: number;
creditsUsedDownload: number;
creditsTotalUsed: number;
compressionRatio: string; // e.g. "34.2%"
}License
MIT © Hydrous
