This project provides a fully extensible ORM-like system designed for use with both in-memory and persistent backends (e.g., Dexie for IndexedDB, GraphQL APIs). It models core ORM principles (inspired by Django) such as Model, QuerySet, Field abstraction, SignalRegistry, and registry-based model relationships.
pre_save, post_save, etc.)npm install --save linquery
import { Model, RelationField, modelRegistry, QuerySet } from "linquery"
import { MemoryBackend } from "linquery/backends/memory"
class Group extends Model {
id
name
static backend = new MemoryBackend()
static objects = new QuerySet(Group, Group.backend)
}
class User extends Model {
id
name
age
group
static fields = {
group: RelationField("Group"),
}
static backend = new MemoryBackend()
static objects = new QuerySet(User, User.backend)
}
modelRegistry.register(Group)
modelRegistry.register(User)
const g = Group.new({id: "g1", name: "Dev"})
await g.save()
const u = User.new({id: "u1", name: "Ana", age: 20, group: {id: "g1"}})
await u.save()
const u2 = User.new({id: "u2", name: "Bia", age: 16, group: {id: "g1"}})
await u2.save()
const results = await User.objects.filter({ name: "Ana" }).execute()
const count = await User.objects.filter({ age: { gte: 18 }}).count()
const users = await User.objects
.filter({ name: { contains: "A" } })
.orderBy("-age")
.limit(5)
.execute()
const group = await user.getRelated("group")
const users = await group.getRelatedMany("User", "group")
User.signals.on("pre_save", User, async (instance) => {
console.log("Saving user", instance.name)
})
import { MemoryBackend } from "linquery/backends/memory"
const memoryBackend = new MemoryBackend<User, Filter<User>>()
import { DexieBackend } from "linquery/backends/dexie"
const dexieBackend = new DexieBackend(User, {
tableName: "users",
indexes: ["name", "age"]
})
import { Graphql } from "linquery/backends/graphql"
const gqlBackend = new Graphql<User, UserFilter, UserOrder>((params) => {
return client.getUsers(params) // Your GraphQL client call
}, User)
const filtered = await User.objects.filter({
OR: {
name: "Ana",
OR: { name: "Bia" }
},
age: { gte: 18 }
}).execute()
If you don’t declare static fields, they will be inferred from class properties.
npm test
Or if using Vitest:
npx vitest run
All major features are covered by tests:
MIT
Contributions welcome!