Skip to content

Powerful and lightweight query abstraction layer inspired by Django ORM. Supports filtering, ordering, joins, related lookups, and multiple backends

Notifications You must be signed in to change notification settings

thiago/linquery

Repository files navigation

ORM-like Query System for JavaScript/TypeScript

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.

Features

  • Declarative model and field system
  • Powerful filtering and lookup API (inspired by Django)
  • Backends for memory, Dexie (IndexedDB), and GraphQL
  • Signal hooks (pre_save, post_save, etc.)
  • Dynamic model registration and relationship inference
  • Testable, extendable, and backend-agnostic architecture

Installation

npm install --save linquery

Example: Defining a Model

import{Model,RelationField,modelRegistry,QuerySet}from"linquery"import{MemoryBackend}from"linquery/backends/memory"classGroupextendsModel{idnamestaticbackend=newMemoryBackend()staticobjects=newQuerySet(Group,Group.backend)}classUserextendsModel{idnameagegroupstaticfields={group: RelationField("Group"),}staticbackend=newMemoryBackend()staticobjects=newQuerySet(User,User.backend)}modelRegistry.register(Group)modelRegistry.register(User)

Creating Instances

constg=Group.new({id: "g1",name: "Dev"})awaitg.save()constu=User.new({id: "u1",name: "Ana",age: 20,group: {id: "g1"}})awaitu.save()constu2=User.new({id: "u2",name: "Bia",age: 16,group: {id: "g1"}})awaitu2.save()

Querying Data

constresults=awaitUser.objects.filter({name: "Ana"}).execute()constcount=awaitUser.objects.filter({age: {gte: 18}}).count()

Chained Filters & Ordering

constusers=awaitUser.objects.filter({name: {contains: "A"}}).orderBy("-age").limit(5).execute()

Fetching Relationships

constgroup=awaituser.getRelated("group")constusers=awaitgroup.getRelatedMany("User","group")

Signals

User.signals.on("pre_save",User,async(instance)=>{console.log("Saving user",instance.name)})

Backends

MemoryBackend

import{MemoryBackend}from"linquery/backends/memory"constmemoryBackend=newMemoryBackend<User,Filter<User>>()

DexieBackend

import{DexieBackend}from"linquery/backends/dexie"constdexieBackend=newDexieBackend(User,{tableName: "users",indexes: ["name","age"]})

GraphQLBackend

import{Graphql}from"linquery/backends/graphql"constgqlBackend=newGraphql<User,UserFilter,UserOrder>((params)=>{returnclient.getUsers(params)// Your GraphQL client call},User)

Advanced Filtering

constfiltered=awaitUser.objects.filter({OR: {name: "Ana",OR: {name: "Bia"}},age: {gte: 18}}).execute()

Inferred Fields

If you don’t declare static fields, they will be inferred from class properties.


Running Tests

npm test

Or if using Vitest:

npx vitest run

All major features are covered by tests:

  • Field serialization/deserialization
  • Query chaining (filter, exclude, order, limit)
  • Relationship resolution
  • Lifecycle signals

License

MIT


TODOs & Contributions

  • Add mutation support to GraphQL backend
  • Add validation system for fields
  • Add support for many-to-many relationships

Contributions welcome!

About

Powerful and lightweight query abstraction layer inspired by Django ORM. Supports filtering, ordering, joins, related lookups, and multiple backends

Resources

Stars

Watchers

Forks

Packages

No packages published