npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

typed-query-filter

v1.0.8

Published

Type-safe query filter engine for array of objects (MongoDB-like syntax)

Readme

🧠 typed-query-filter

Type-safe, MongoDB-like query engine for JavaScript/TypeScript arrays — built for both frontend & backend.


✨ Features

  • ✅ Type-safe query engine for object arrays
  • ✅ MongoDB-like operators: $eq, $gt, $in, $regex, $elemMatch, $and, $or, $where, etc.
  • ✅ Intuitive nested object queries without requiring $elemMatch for simple cases
  • ✅ Enhanced regex support with multiple pattern formats
  • QueryBuilder<T> chain API with .where(), .and(), .or(), .custom()
  • .sort(), .limit(), .select() just like MongoDB
  • ✅ Nested field access with autocomplete (user.address.city)
  • ✅ Built-in type-safe helper: FlatKey<T>
  • ✅ Flexible projection with 3 different select() methods
  • ✅ Template-based array transformation
  • ✅ Optimized for deep recursive queries without stack overflows
  • ✅ Optional WebAssembly backend for high-performance filtering (coming soon)
  • ✅ Runs in both Node.js and modern browsers

📦 Install

npm install typed-query-filter
# or
yarn add typed-query-filter

🔧 Usage

Example type:

interface User {
  name: string;
  age: number;
  hobbies: string[];
  posts: { 
    title: string; 
    likes: number;
    details: {
      tags: string[];
      comments: { user: string; text: string }[];
    }
  }[];
}

const users: User[] = [
  { name: 'Alice', age: 25, hobbies: ['dev'], posts: [{ title: 'TS', likes: 10, details: {tags: ['typescript'], comments: [{user: 'Bob', text: 'Great!'}]} }] },
  { name: 'Bob', age: 30, hobbies: ['test'], posts: [{ title: 'Rust', likes: 5, details: {tags: ['rust'], comments: [{user: 'Alice', text: 'Cool!'}]} }] },
  { name: 'Charlie', age: 22, hobbies: ['dev', 'blog'], posts: [{ title: 'TS', likes: 50, details: {tags: ['typescript', 'javascript'], comments: [{user: 'Dave', text: 'Nice!'}]} }] },
];

👉 Basic Query

import { filterData } from 'typed-query-filter';

const result = filterData(users, {
  age: { $gte: 25 },
  hobbies: { $in: ['dev'] }
});

👉 With QueryBuilder

import { QueryBuilder } from 'typed-query-filter';

const qb = new QueryBuilder<User>()
  .where('age', { $gt: 20 })
  .where('posts', {
    $elemMatch: {
      likes: { $gte: 10 },
      'details.tags': { $some: ['javascript'] }
    }
  })
  .custom((doc) => doc.name.startsWith('C'))
  .sort('age', 'desc')
  .limit(1)
  .select('name', 'age');

const result = qb.filter(users);

🔎 Supported Operators

| Operator | Description | |---------------|------------------------------------| | $eq, $ne | Equality / inequality | | $gt, $gte | Greater than / or equal | | $lt, $lte | Less than / or equal | | $in, $nin | Value is (not) in array | | $regex | Regex string match (supports RegExp objects, string patterns, and flags) | | $exists | Field is defined or not | | $not | Invert a condition | | $all | All values in array match | | $size | Array has specific length | | $elemMatch | Some element in array matches query | | $and, $or | Combine multiple conditions | | $where | Custom (doc) => boolean function | | $some | match with some element in array |


🔁 Projection with select()

1️⃣ Select specific fields

// Select just the fields you need
const result = new QueryBuilder<User>()
  .where('age', { $gt: 20 })
  .select('name', 'age', 'hobbies')
  .filter(users);

// Result:
// [
//   { name: 'Alice', age: 25, hobbies: ['dev'] },
//   { name: 'Bob', age: 30, hobbies: ['test'] },
//   { name: 'Charlie', age: 22, hobbies: ['dev', 'blog'] }
// ]

2️⃣ Transform with callback function

// Fully transform results with a callback function
const result = new QueryBuilder<User>()
  .where('age', { $gt: 20 })
  .select(user => ({
    fullName: user.name,
    ageInMonths: user.age * 12,
    primaryHobby: user.hobbies[0] || 'none',
    postCount: user.posts.length,
    mostLikedPost: user.posts.length > 0 
      ? user.posts.sort((a, b) => b.likes - a.likes)[0].title
      : null
  }))
  .filter(users);

// Result:
// [
//   { 
//     fullName: 'Alice', 
//     ageInMonths: 300, 
//     primaryHobby: 'dev',
//     postCount: 1,
//     mostLikedPost: 'TS'
//   },
//   // ...
// ]

3️⃣ Use template object

// Use an object template to reshape your data
const result = new QueryBuilder<User>()
  .where('age', { $gt: 20 })
  .select({
    fullName: 'name',
    years: 'age',
    skills: 'hobbies',
    isActive: true,  // Static value
    firstPost: 'posts[0].title'  // Array indexing
  })
  .filter(users);

// Result:
// [
//   { 
//     fullName: 'Alice', 
//     years: 25, 
//     skills: ['dev'],
//     isActive: true,
//     firstPost: 'TS'
//   },
//   // ...
// ]

✨ Array Templates for Nested Transformations

For complex nested arrays, use the array template syntax:

const result = new QueryBuilder<User>()
  .where('age', { $gt: 20 })
  .select({
    name: 'name',
    age: 'age',
    // Transform the posts array
    posts: ['posts', {
      title: 'title',
      likeCount: 'likes',
      // Transform the nested comments array
      comments: ['details.comments', {
        author: 'user',
        message: 'text'
      }],
      tags: 'details.tags'
    }]
  })
  .filter(users);

// Result:
// [
//   { 
//     name: 'Alice',
//     age: 25,
//     posts: [
//       {
//         title: 'TS',
//         likeCount: 10,
//         comments: [
//           { author: 'Bob', message: 'Great!' }
//         ],
//         tags: ['typescript']
//       }
//     ]
//   },
//   // ...
// ]

🔁 Advanced Usage

📚 FlatKey<T> helper for nested field paths

import { getFlatKeys } from 'typed-query-filter';

const fields = getFlatKeys<User>({
  name: '',
  age: 0,
  hobbies: [''],
  posts: [{ title: '', likes: 0 }]
});

// → ['name', 'age', 'hobbies', 'posts', 'posts.title', 'posts.likes']

Useful for building dynamic filter UI with autocomplete

📐 Intuitive Nested Object Queries

You can now query nested arrays of objects without using $elemMatch for simple cases:

// Traditional query with $elemMatch
const traditionalResult = filterData(users, { 
  posts: { 
    $elemMatch: { 
      likes: { $gt: 100 },
      'details.published': true
    } 
  } 
});

// New intuitive syntax (does the same thing)
const intuitiveResult = filterData(users, {
  posts: {
    likes: { $gt: 100 },
    'details.published': true
  }
});

🔍 Enhanced RegEx Support

Multiple ways to use regex patterns:

// Using RegExp objects
filterData(users, { name: { $regex: /^[AB]/ } });

// Using string patterns
filterData(users, { name: { $regex: '^[AB]' } });

// Using string patterns with flags (format: '/pattern/flags')
filterData(users, { name: { $regex: '/alice|bob/i' } });

// Alternative explicit format for flags ('pattern||flags')
filterData(users, { name: { $regex: 'alice|bob||i' } });

🔄 Deep Recursion Handling

The library is optimized to handle deeply nested object queries without causing stack overflow errors:

// Deep nested query that works efficiently
const result = filterData(users, {
  posts: {
    details: {
      comments: {
        user: 'Bob',
        text: { $regex: /Great/ }
      },
      tags: { $some: ['typescript', 'programming'] }
    }
  }
});

🧪 Testing

npm install
npm run test

Using Vitest for blazing-fast testing.


🚀 Coming Soon

  • 🧩 WASM backend for ultra-fast filtering
  • 🌐 Dynamic query parser (JSON-based)
  • 🧱 Form builder integration (e.g., React filter builder)

📄 License

MIT © 2025 DinhPlus


🤝 Contributing

PRs and issues welcome! This project is designed to scale with many types of data structures and support massive filtering use cases (with WASM coming).