a Redis-backed ORM with Zod validation, automatic
id/createdAt/updatedAt, hooks, and optional secondary indexes (set and
zset).
# Using Deno
deno add jsr:@simkit/kv-orm npm:ioredis npm:zod
# Using pnpm (bun, npm)
pnpm add jsr:@simkit/kv-orm ioredis zodfull example: example.ts
import { KvOrm, KvOrmSchema } from "@simkit/kv-orm";
import { z } from "zod";
import Redis from "ioredis";
// Initialize Redis
const redis = new Redis();
// Define a schema
const userSchema = KvOrmSchema({
email: z.string().email(),
name: z.string(),
});
// Create KvOrm instance
const usersOrm = new KvOrm({
prefix: "user",
kv: redis,
schema: userSchema,
indexedFields: { email: "set" }, // optional secondary indexes
});Note:
id,createdAt, andupdatedAtare automatically added and cannot be redefined.
Create a new entity.
const user = await usersOrm.create({
email: "alice@example.com",
name: "Alice",
});data: input object matching schemaoptions: optional hooks{ hooks?: { before?, after? } }
Create multiple entities at once.
const newUsers = await usersOrm.createBulk([
{ email: "bob@example.com", name: "Bob" },
{ email: "carol@example.com", name: "Carol" },
]);Get an entity by ID.
const user = await usersOrm.get("uuid-of-user");Get an entity by ID, returns null if not found.
const user = await usersOrm.maybeGet("uuid-that-may-not-exist");Get all entities, optionally filtered by key pattern.
const allUsers = await usersOrm.getAll(); // default to "*"Query entities by field (supports indexed fields).
const result = await usersOrm.findWhere("email", "eq", "alice@example.com");-
operatorcan be:- numbers/dates:
eq | ne | lt | lte | gt | gte | in | nin - strings:
eq | ne | like | in | nin - others:
eq | ne | in | nin
- numbers/dates:
Update an entity partially, returns updated entity or null.
const updatedUser = await usersOrm.update(user.id, { name: "Alice Smith" });Update an entity, throws if not found.
const updatedUser = await usersOrm.updateOrFail(user.id, {
name: "Alice Smith",
});Delete an entity by ID.
const deleted = await usersOrm.delete(user.id); // true if deletedDelete multiple entities matching a key pattern.
const count = await usersOrm.deleteAll(); // default to "*" and returns number of deleted entitiesAdd global or dynamic hooks for any method.
users.addHooks({
create: {
before: async ({ input }) => console.log("Creating", input),
after: async ({ result }) => console.log("Created", result),
},
});Rebuild all secondary indexes (useful after bulk operations or corruption).
await usersOrm.rebuildIndexes();Use KvOrmSchema to automatically add required fields (id, createdAt,
updatedAt):
import { KvOrmSchema } from "kv-orm";
import { z } from "zod";
const productSchema = KvOrmSchema({
name: z.string(),
price: z.number(),
});Important: Do not redefine
id,createdAt, orupdatedAt. KvOrm will throw a runtime error if they are included.
All methods support before/after hooks for custom logic:
const hooks = {
create: {
before: async ({ input }) => {/* validate, modify input */},
after: async ({ result }) => {/* log or trigger events */},
},
};
usersOrm.addHooks(hooks);You can define fields for fast queries:
"set"→ equality queries (eq,ne,in,nin)"zset"→ range queries (lt,lte,gt,gte)
const usersOrm = new KvOrm({
prefix: "user",
kv: redis,
schema: userSchema,
indexedFields: {
email: "set",
createdAt: "zset",
},
});- Migration System – Support for schema migrations and versioning of entities.
- Query Pagination – Add
offsetandlimitparameters forgetAllandfindWherequeries. - Advanced Indexing – Support compound and multi-field indexes.
- Batch Operations – Further optimizations for large bulk inserts/updates.
- TypeScript Enhancements – Explore stricter compile-time checks for reserved fields without runtime errors.
Contributions and suggestions are welcome!
MIT