TL;DR
Use MongoDB's familiar query syntax ($gt, $in, $regex) to filter JavaScript arrays with full TypeScript type safety:
import { filter } from '@mcabreradev/filter';
// Instead of this ❌
products.filter(p => p.price > 100 && p.price < 500 && p.inStock)
// Write this ✅
filter(products, {
price: { $gte: 100, $lte: 500 },
inStock: true
})
Benefits: 18+ operators, zero dependencies, lazy evaluation, framework integrations (React/Vue/Svelte), 100% type-safe. Try it live →
MongoDB-Style Filtering in TypeScript with @mcabreradev/filter
If you've ever worked with MongoDB, you know how powerful and intuitive its query operators are. What if you could use that same familiar syntax for filtering JavaScript arrays and objects? That's exactly what @mcabreradev/filter enables!
In this post, I'll show you how to leverage MongoDB-style operators for type-safe filtering in TypeScript.
🎯 Why MongoDB-Style Filtering?
MongoDB's query language is:
-
Intuitive: Familiar operators like
$gt,$in,$regex - Powerful: Complex nested queries with logical operators
- Expressive: Clear intent with declarative syntax
Now you can use this same approach in your TypeScript applications, with full type safety!
🚀 Quick Start
First, install the package:
npm install @mcabreradev/filter
# or
pnpm add @mcabreradev/filter
# or
yarn add @mcabreradev/filter
📊 Basic MongoDB-Style Queries
Comparison Operators
Just like MongoDB, you can use familiar comparison operators:
import { filter } from '@mcabreradev/filter';
interface Product {
name: string;
price: number;
stock: number;
rating: number;
}
const products: Product[] = [
{ name: 'Laptop', price: 999, stock: 5, rating: 4.5 },
{ name: 'Mouse', price: 25, stock: 50, rating: 4.2 },
{ name: 'Keyboard', price: 75, stock: 30, rating: 4.7 },
{ name: 'Monitor', price: 299, stock: 0, rating: 4.3 },
];
// Greater than operator ($gt)
const expensive = filter(products, {
price: { $gt: 100 }
});
// Result: [Laptop, Monitor]
// Less than or equal ($lte)
const affordable = filter(products, {
price: { $lte: 100 }
});
// Result: [Mouse, Keyboard]
// Not equal ($ne)
const inStock = filter(products, {
stock: { $ne: 0 }
});
// Result: [Laptop, Mouse, Keyboard]
// Between values (combining operators)
const midRange = filter(products, {
price: { $gte: 50, $lte: 300 }
});
// Result: [Keyboard, Monitor]
Array Operators
Work with arrays just like in MongoDB:
interface User {
name: string;
age: number;
tags: string[];
favoriteColors: string[];
}
const users: User[] = [
{ name: 'Alice', age: 25, tags: ['admin', 'developer'], favoriteColors: ['blue', 'green'] },
{ name: 'Bob', age: 30, tags: ['developer'], favoriteColors: ['red'] },
{ name: 'Charlie', age: 35, tags: ['designer', 'developer'], favoriteColors: ['blue', 'yellow'] },
];
// $in operator - value in array
const youngUsers = filter(users, {
age: { $in: [25, 30] }
});
// Result: [Alice, Bob]
// $contains - array contains value
const developers = filter(users, {
tags: { $contains: 'developer' }
});
// Result: [Alice, Bob, Charlie]
// $all - array contains all values
const adminDevelopers = filter(users, {
tags: { $all: ['admin', 'developer'] }
});
// Result: [Alice]
// $size - array length
const multiColor = filter(users, {
favoriteColors: { $size: 2 }
});
// Result: [Alice, Charlie]
String Operators
MongoDB-style string matching with full regex support:
interface Article {
title: string;
content: string;
author: string;
}
const articles: Article[] = [
{ title: 'Getting Started with TypeScript', content: '...', author: 'John Doe' },
{ title: 'Advanced TypeScript Patterns', content: '...', author: 'Jane Smith' },
{ title: 'JavaScript Best Practices', content: '...', author: 'John Adams' },
];
// $regex operator
const tsArticles = filter(articles, {
title: { $regex: /typescript/i }
});
// Result: First two articles
// $startsWith
const gettingStarted = filter(articles, {
title: { $startsWith: 'Getting' }
});
// $endsWith
const patterns = filter(articles, {
title: { $endsWith: 'Patterns' }
});
// $contains (case-insensitive by default)
const johnAuthors = filter(articles, {
author: { $contains: 'john' }
});
// Result: [John Doe, John Adams]
🎭 Logical Operators
Combine conditions just like MongoDB's $and, $or, $not:
interface Task {
title: string;
priority: 'low' | 'medium' | 'high';
completed: boolean;
assignee: string;
dueDate: Date;
}
const tasks: Task[] = [
{ title: 'Fix bug #123', priority: 'high', completed: false, assignee: 'Alice', dueDate: new Date('2025-10-30') },
{ title: 'Write docs', priority: 'medium', completed: true, assignee: 'Bob', dueDate: new Date('2025-10-29') },
{ title: 'Code review', priority: 'low', completed: false, assignee: 'Alice', dueDate: new Date('2025-10-28') },
];
// $and - all conditions must match
const urgentTasks = filter(tasks, {
$and: [
{ priority: 'high' },
{ completed: false }
]
});
// $or - at least one condition must match
const aliceOrHighPriority = filter(tasks, {
$or: [
{ assignee: 'Alice' },
{ priority: 'high' }
]
});
// $not - negation
const notCompleted = filter(tasks, {
$not: { completed: true }
});
// Complex nested queries
const criticalTasks = filter(tasks, {
$and: [
{
$or: [
{ priority: 'high' },
{ dueDate: { $lte: new Date('2025-10-29') } }
]
},
{ completed: false }
]
});
🏢 Real-World Example: E-commerce Product Filter
Here's a comprehensive example combining multiple operators:
import { filter, createConfig } from '@mcabreradev/filter';
interface Product {
id: string;
name: string;
category: string;
price: number;
rating: number;
tags: string[];
inStock: boolean;
brand: string;
reviews: number;
}
const products: Product[] = [
{ id: '1', name: 'Gaming Laptop Pro', category: 'Electronics', price: 1299, rating: 4.7, tags: ['gaming', 'laptop', 'high-performance'], inStock: true, brand: 'TechCorp', reviews: 342 },
{ id: '2', name: 'Wireless Mouse', category: 'Accessories', price: 29, rating: 4.3, tags: ['wireless', 'ergonomic'], inStock: true, brand: 'TechCorp', reviews: 128 },
{ id: '3', name: 'Mechanical Keyboard', category: 'Accessories', price: 89, rating: 4.6, tags: ['mechanical', 'rgb', 'gaming'], inStock: false, brand: 'KeyMaster', reviews: 256 },
{ id: '4', name: 'USB-C Hub', category: 'Accessories', price: 45, rating: 4.1, tags: ['usb-c', 'hub'], inStock: true, brand: 'TechCorp', reviews: 89 },
];
// Complex product search with MongoDB-style operators
const searchResults = filter(products, {
$and: [
// Must be in stock
{ inStock: true },
// Price range: $20 - $100
{ price: { $gte: 20, $lte: 100 } },
// At least one matching tag
{
$or: [
{ tags: { $contains: 'gaming' } },
{ tags: { $contains: 'wireless' } },
{ tags: { $contains: 'ergonomic' } }
]
},
// Good ratings
{ rating: { $gte: 4.0 } },
// Popular products
{ reviews: { $gt: 100 } }
]
});
// Result: [Wireless Mouse]
console.log(searchResults);
⚡ Performance with Lazy Evaluation
For large datasets, use lazy evaluation to process items on-demand:
import { filterLazy } from '@mcabreradev/filter';
const hugeProductList = [...]; // 100,000 products
// Lazy evaluation - only processes until we find what we need
const firstExpensive = filterLazy(hugeProductList, {
$and: [
{ price: { $gt: 1000 } },
{ rating: { $gte: 4.5 } },
{ inStock: true }
]
}).take(1);
// Only processes items until first match is found!
console.log(firstExpensive);
🎨 Framework Integration
React Hook Example
import { useFilter } from '@mcabreradev/filter/react';
function ProductList() {
const [products, setProducts] = useState<Product[]>([]);
const {
filtered,
setQuery,
stats
} = useFilter(products, {
debug: true,
memoize: true
});
const searchExpensive = () => {
setQuery({
$and: [
{ price: { $gt: 500 } },
{ rating: { $gte: 4.0 } },
{ inStock: true }
]
});
};
return (
<div>
<button onClick={searchExpensive}>
Show Premium Products
</button>
<div>Found: {stats.totalProcessed} products</div>
{filtered.map(product => (
<ProductCard key={product.id} product={product} />
))}
</div>
);
}
Vue Composable Example
import { useFilter } from '@mcabreradev/filter/vue';
import { ref } from 'vue';
export function useProductSearch() {
const products = ref<Product[]>([]);
const { filtered, setQuery, stats } = useFilter(products, {
memoize: true
});
const searchByCategory = (category: string) => {
setQuery({
$and: [
{ category },
{ inStock: true },
{ rating: { $gte: 4.0 } }
]
});
};
return {
products: filtered,
searchByCategory,
stats
};
}
🐛 Debugging Your Queries
Enable debug mode to see exactly how your queries are processed:
import { filter, createConfig } from '@mcabreradev/filter';
const config = createConfig({
debug: true,
verbose: true
});
const results = filter(products, {
$and: [
{ price: { $gt: 100 } },
{ tags: { $contains: 'gaming' } }
]
}, config);
// Console output:
// ┌─ Filter Execution Tree
// │ Root (AND)
// │ ├─ price.$gt(100) ✓
// │ └─ tags.$contains("gaming") ✓
// └─ Results: 1 item(s) in 0.234ms
🔧 Custom Operators
Extend the library with your own MongoDB-style operators:
import { createConfig } from '@mcabreradev/filter';
const config = createConfig({
customOperators: {
$discounted: (value: number, threshold: number) => {
const discount = (value.price - value.salePrice) / value.price;
return discount >= threshold;
},
$trending: (value: Product) => {
return value.views > 1000 && value.recentSales > 50;
}
}
});
const deals = filter(products, {
$and: [
{ $discounted: 0.2 }, // 20% off or more
{ $trending: true }
]
}, config);
📊 Comparison with Native Array Methods
// ❌ Native JavaScript - verbose and hard to read
const filtered = products
.filter(p => p.price > 100 && p.price < 500)
.filter(p => p.inStock)
.filter(p => p.tags.includes('gaming') || p.tags.includes('professional'))
.filter(p => p.rating >= 4.5);
// ✅ MongoDB-style - clean and declarative
const filtered = filter(products, {
$and: [
{ price: { $gt: 100, $lt: 500 } },
{ inStock: true },
{ tags: { $in: ['gaming', 'professional'] } },
{ rating: { $gte: 4.5 } }
]
});
🎯 Type Safety
Full TypeScript support with autocomplete:
// TypeScript knows your data structure
const filtered = filter<Product>(products, {
// ✅ Autocomplete for properties
price: { $gt: 100 },
// ❌ TypeScript error - invalid property
invalidProp: { $eq: 'test' },
// ✅ Type-safe operators
rating: { $gte: 4.0 },
// ❌ TypeScript error - wrong operator for type
price: { $contains: 'test' }
});
🚀 Performance Tips
- Use memoization for repeated queries:
const config = createConfig({ memoize: true });
- Lazy evaluation for large datasets:
const results = filterLazy(bigData, query).take(10);
- Index frequently queried fields (coming soon in v3.0)
🔗 Resources
🎉 Try It Now!
npm install @mcabreradev/filter
Start filtering your data with MongoDB-style operators today! The library is:
- ✅ 100% TypeScript with full type safety
- ✅ Zero dependencies
- ✅ Tree-shakeable - only bundle what you use
- ✅ Framework agnostic - works with React, Vue, Svelte, and more
- ✅ Fully tested - 100% code coverage
💬 What's Next?
In the next post, I'll cover:
- Advanced nested object filtering
- Performance benchmarks vs other libraries
- Building a full-stack search with MongoDB backend
Have questions or suggestions? Drop a comment below! 👇
If you found this helpful, give it a ❤️ and follow for more TypeScript tips!
Top comments (0)