Getting Started with Lioran S3 in TypeScript
This tutorial walks through the shortest practical path from a clean Node.js project to uploading and reading objects with Lioran S3 / Lioran Bastion.
Lioran S3 V1 Pre-Alpha launched on October 1, 2026. It is developed by Lioran Developer Solutions (LDS) under Lioran Group, led by Founder & CTO Swaraj Puppalwar.
This is pre-alpha infrastructure. Pin versions and test carefully.
Requirements
Use:
- Node.js 18 or newer
- TypeScript 5 or newer
- a running Lioran S3 instance
- valid user credentials or an access key
Create a project:
mkdir liorans3-demo
cd liorans3-demo
npm init -y
npm install @liorans3/driver@prealpha
npm install -D typescript tsx @types/node
Initialize TypeScript:
npx tsc --init
Store the connection URI
Create .env:
LIORAN_S3_URI=bastion://admin:YOUR_PASSWORD@127.0.0.1:27118
Do not commit credentials.
Connect
Create src/index.ts:
import { BastionClient } from "@liorans3/driver";
const client = new BastionClient(
process.env.LIORAN_S3_URI ??
"bastion://admin:YOUR_PASSWORD@127.0.0.1:27118"
);
const health = await client.health();
console.log({
name: health.name,
version: health.version,
});
Run it:
npx tsx src/index.ts
URI behavior
The driver accepts Bastion URIs:
bastion://username:password@hostname[:port][/default_bucket]
For local/private addresses, the driver normally resolves to HTTP and the native Bastion port.
For public domains, it can resolve to HTTPS.
You may also use structured configuration:
const client = new BastionClient({
username: "admin",
password: process.env.LIORAN_PASSWORD!,
host: "storage.example.com",
isTls: true,
});
Or access-key authentication:
const client = new BastionClient({
accessKey: process.env.LIORAN_ACCESS_KEY!,
secretKey: process.env.LIORAN_SECRET_KEY!,
host: "storage.example.com",
isTls: true,
});
Create a bucket
await client.buckets.create("demo", {
quotaBytes: 10 * 1024 * 1024 * 1024,
});
Get a bucket-scoped handle:
const bucket = client.bucket("demo");
Upload text
await bucket.put(
"notes/hello.txt",
"Hello from Lioran S3",
{
contentType: "text/plain",
}
);
Upload JSON
const payload = {
service: "liorans3",
stage: "pre-alpha",
launched: "2026-10-01",
};
await bucket.put(
"config/app.json",
JSON.stringify(payload),
{
contentType: "application/json",
}
);
Upload a file as a stream
This is the preferred pattern for large files because the object does not need to be loaded completely into memory.
import fs from "node:fs";
const input = fs.createReadStream("./sample.zip");
const result = await bucket.put(
"archives/sample.zip",
input,
{
contentType: "application/zip",
}
);
console.log({
key: result.key,
size: result.size_bytes,
sha256: result.sha256,
});
Download an object
const object = await bucket.get("notes/hello.txt");
console.log(await object.text());
Write directly to disk:
const archive = await bucket.get("archives/sample.zip");
await archive.writeToFile("./downloads/sample.zip");
Read JSON:
type AppConfig = {
service: string;
stage: string;
launched: string;
};
const object = await bucket.get("config/app.json");
const config = await object.json<AppConfig>();
console.log(config.stage);
Inspect metadata without downloading
const metadata = await bucket.head("archives/sample.zip");
console.log({
contentType: metadata.contentType,
contentLength: metadata.contentLength,
etag: metadata.etag,
sha256: metadata.sha256,
});
Delete an object
await bucket.delete("notes/hello.txt");
List objects
Fetch one page:
const page = await bucket.listPage({
prefix: "archives/",
limit: 100,
});
console.log(page.objects);
console.log(page.has_more);
console.log(page.next_cursor);
Or let the driver paginate:
const objects = await bucket.list({
prefix: "archives/",
});
console.log(objects);
Error handling
The driver exposes structured error classes.
import {
BastionAuthError,
BastionError,
ConnectionError,
NotFoundError,
QuotaExceededError,
} from "@liorans3/driver";
try {
await bucket.get("does-not-exist.txt");
} catch (error) {
if (error instanceof NotFoundError) {
console.error("Object not found");
} else if (error instanceof QuotaExceededError) {
console.error("Bucket quota exceeded");
} else if (error instanceof BastionAuthError) {
console.error("Authentication failed");
} else if (error instanceof ConnectionError) {
console.error(error.safeUri);
} else if (error instanceof BastionError) {
console.error(error.code, error.statusCode, error.message);
} else {
throw error;
}
}
Recommended project structure
src/
├── storage/
│ ├── client.ts
│ ├── uploads.ts
│ └── downloads.ts
└── index.ts
client.ts:
import { BastionClient } from "@liorans3/driver";
export const storage = new BastionClient(
process.env.LIORAN_S3_URI!
);
That keeps connection construction in one place and prevents credentials from spreading through your codebase.
Next
The next article covers bucket administration, object CRUD, pagination, range reads, and server-side image transformations in more detail.
Documentation: https://docs.liorans3.sbs
Top comments (0)