@farming-labs/orm

Unstorage

Unstorage support in Farming Labs ORM is the key-value/runtime answer for teams that want one storage layer across lightweight stores without owning a separate adapter surface for each Unstorage driver.

The supported path is a raw Unstorage storage instance created with createStorage(...).

That makes it a good fit for:

It is not the runtime to reach for when the workload is highly relational, join-heavy, or dependent on strong database transaction guarantees.

What this gives you

You still write the schema and storage layer once.

Then the runtime translates that layer into Unstorage operations with:

Create the runtime directly

unstorage-runtime.ts
import { createOrm } from "@farming-labs/orm";
import { createUnstorageDriver } from "@farming-labs/orm-unstorage";
import { createStorage } from "unstorage";
import memoryDriver from "unstorage/drivers/memory";
import { appSchema } from "./schema";

const storage = createStorage({
  driver: memoryDriver(),
});

const orm = createOrm({
  schema: appSchema,
  driver: createUnstorageDriver({
    storage,
  }),
});

You can use any Unstorage storage instance that exposes the standard getItem(...), setItem(...), removeItem(...), and getKeys(...) shape.

Use the runtime helper path

If a framework or shared package wants to accept a raw Unstorage instance and normalize it later, use the runtime helpers instead:

import { createOrmFromRuntime } from "@farming-labs/orm-runtime";

const orm = await createOrmFromRuntime({
  schema: appSchema,
  client: storage,
});

That keeps the package boundary generic instead of forcing a storage-specific branch.

Setup helpers

The setup helpers are intentionally conservative here:

import { bootstrapDatabase } from "@farming-labs/orm-runtime/setup";

const orm = await bootstrapDatabase({
  schema: appSchema,
  client: storage,
});

That reflects the real backend shape: Unstorage is a generic key-value/document abstraction, not a SQL-style schema engine.

Joins, relations, and transactions

Unstorage does not have native joins.

That does not mean relation selections stop working. The ORM resolves relation branches through follow-up reads, so the same higher-level storage code can still select related records.

It is still important to be honest about the boundary:

So this runtime works well for shared package logic that needs one consistent storage contract, but it is not the best fit for workloads that are deeply relational or centered around complex join-heavy query plans.

What is supported well

Important limits

Local verification

The repo verifies the Unstorage runtime locally with the in-memory and fs-lite drivers.

Run it with:

terminal
pnpm test:local:unstorage

Why it matters

This keeps Unstorage inside the same bigger ORM story: