Getting Started
S3WORM (S3 Wrapped ORM) turns any S3-compatible bucket into a typed JSON document database. Point it at AWS S3, Storj, MinIO, Cloudflare R2, or DigitalOcean Spaces and get a full ORM with entities, repositories, schemas, oplogs, and more -- no traditional database required.
Install
S3WORM is published to GitHub Packages. Configure your .npmrc first:
# .npmrc (project root or home directory)
@decoperations:registry=https://npm.pkg.github.com
//npm.pkg.github.com/:_authToken=${GITHUB_TOKEN}
Then install:
pnpm add @decoperations/s3worm
Requires Node.js 18 or later.
Create a Client
The S3Worm class is the main entry point. Pass it your bucket name, endpoint, and credentials:
import { S3Worm } from "@decoperations/s3worm";
const worm = new S3Worm({
bucket: "my-app-data",
endpoint: "https://gateway.storjshare.io",
region: "us-east-1",
credentials: {
accessKeyId: process.env.S3_ACCESS_KEY!,
secretAccessKey: process.env.S3_SECRET_KEY!,
},
});
This works with any S3-compatible provider. Storj, AWS, MinIO, R2 -- just change the endpoint.
Define an Entity (Class-Based)
Entities are TypeScript classes that extend Entity. Each entity type declares its storage path via a static getBasePath() method:
import { Entity } from "@decoperations/s3worm";
class BlogPost extends Entity {
title: string = "";
slug: string = "";
content: string = "";
published: boolean = false;
tags: string[] = [];
static getBasePath(): string {
return "posts";
}
}
Every entity automatically gets id, createdAt, and updatedAt fields from the base class. The id is auto-generated on save if not set. Documents are stored as JSON files at {basePath}/{id}.json.
Get a Repository and Do CRUD
Use getRepository() to get a typed repository for any entity class:
const posts = worm.getRepository(BlogPost);
// Create and save
const post = posts.create({
title: "Hello World",
slug: "hello-world",
content: "My first post.",
published: true,
tags: ["intro", "blog"],
});
await posts.save(post);
// post.id is now set (auto-generated nanoid)
// post.createdAt and post.updatedAt are set
// Find by ID
const found = await posts.findById(post.id);
// Find all with filtering, sorting, and pagination
const published = await posts.findAll({
filter: (p) => p.published === true,
sort: (a, b) => b.createdAt!.localeCompare(a.createdAt!),
limit: 10,
offset: 0,
});
// Delete
await posts.delete(post.id);
Schema-Driven Models
For a more powerful approach, define models in a JSON schema and let S3WORM generate everything for you:
const worm = new S3Worm({
bucket: "my-app-data",
endpoint: "https://gateway.storjshare.io",
credentials: { accessKeyId: "...", secretAccessKey: "..." },
});
worm.loadSchema({
schemaVersion: "1.0",
sourceOfTruth: "local",
models: {
Customer: {
path: "#org/@customers/(id:uuid)",
idType: "uuid",
fields: {
name: { type: "string", required: true },
email: { type: "string", required: true },
status: { type: "string", enum: ["active", "inactive"], default: "active" },
},
file: "[profile].json",
mode: "readwrite",
},
},
});
const customers = worm.model("Customer");
// Create
const customer = await customers.save({
name: "Acme Corp",
email: "hello@acme.com",
});
// Query
const active = await customers.findAll({
filter: { status: "active" },
sort: { field: "createdAt", order: "desc" },
limit: 50,
});
// Find by ID
const found = await customers.findById(customer.id);
Schema-driven models give you validation, access modes (readonly/readwrite/append), soft delete, oplogs, snapshots, entity links, and more. See the Entity & Repository and Configuration docs for the full picture.
Run the CLI
The worm CLI manages schemas, lints bucket structures, runs a local dev server, and generates code:
# Initialize a new .worm directory with a default schema
npx worm init
# Validate your schema
npx worm lint
# Start local dev mode (filesystem-backed, no S3 needed)
npx worm dev
# Generate TypeScript interfaces from your schema
npx worm codegen
# Generate TypeScript + Zod validation schemas
npx worm codegen --zod
# Generate llms.txt for AI tool consumption
npx worm llms --output llms.txt
The worm dev command starts a local development server backed by the filesystem instead of S3, so you can iterate on your schema without needing cloud credentials.
Coming soon:
worm push,worm pull,worm diff,worm status, andworm serveare planned for upcoming releases. See the Roadmap for details.
What's Next
| Topic | Description |
|---|---|
| Installation | Detailed setup for all environments |
| Configuration | Provider configs, CORS, environment variables |
| S3Worm Client | Full API reference for the S3Worm class |
| Entity & Repository | Deep dive into entities and repositories |
| Path DSL | How the path language maps to S3 keys |