A Coffee-Break Guide to FilterQL: The Tiny Language for Filtering Any Structured Data

Turn 1,000 movie rows into “Action or Comedy, 8.5+ rating, post-2000, top-10 by score” with one line:

(genre == Action || genre == Comedy) && year >= 2000 && rating >= 8.5 | SORT rating desc | LIMIT 10

If you have ever typed a WHERE clause in SQL, chained .filter() in JavaScript, or simply wished your REST API payload were smaller before it hits the browser, FilterQL is the pocket-sized tool built for you.

This post walks you through everything contained in the official FilterQL repository—nothing more, nothing less. You will learn how to install it, how to read and write queries, how to embed it in a CLI or web app, and how to extend it with your own commands. All examples are copy-paste ready and aimed at readers who have at least a junior-college background but do not want to wade through academic papers.


Contents

  1. What Exactly Is FilterQL?
  2. Installation & 30-Second Kick-Start
  3. Quick-Reference Cheat Sheet
  4. Real-World Example: A Universal CLI Filter
  5. Deep Dive in Plain Code

    • Declaring a schema
    • Writing queries
    • Chaining operations (sort, limit, custom)
  6. Frequently Asked Questions
  7. Advanced: Rolling Your Own ROUND or GROUP Operation
  8. Language Specification in One Screen
  9. Recap & Next Steps

1. What Exactly Is FilterQL?

FilterQL is two things:

  • A 3 KB TypeScript library you install with npm or bun.
  • An open grammar specification that anyone can re-implement in Python, Go, Rust, or any other language.

You give it an array of plain JavaScript objects and a short query string. It gives back the rows that match, already sorted or trimmed to your taste.

No external servers. No query planner. No learning curve steeper than writing an email.


2. Installation & 30-Second Kick-Start

Install

# Bun (fastest)
bun add filterql

# npm / pnpm / yarn
npm install filterql

Smallest Possible Program

// demo.ts
import { FilterQL } from "filterql"

// 1. Tell FilterQL which fields exist
const schema = {
  title:  { type: "string", alias: "t" },
  year:   { type: "number", alias: "y" },
  rating: { type: "number" },
  genre:  { type: "string" },
  watched:{ type: "boolean", alias: "w" }
}

// 2. Create the engine
const fql = new FilterQL({ schema })

// 3. Your raw data (from API, file, wherever)
const movies = [
  { title: "The Matrix", year: 1999, rating: 8.7, genre: "Action", watched: true },
  { title: "Inception",  year: 2010, rating: 8.8, genre: "Sci-Fi", watched: false },
  { title: "The Dark Knight", year: 2008, rating: 9.0, genre: "Action", watched: true }
]

// 4. One-liner query
const result = fql.filter(movies, "genre == Action && watched | SORT rating desc")

console.table(result)

Run it:

bun demo.ts

You will see:

┌---------┬-------------------┬------┬--------┬--------┬---------┐
│ (index) │      title        │ year │ rating │ genre  │ watched │
├---------┼-------------------┼------┼--------┼--------┼---------┤
│    0    │ "The Dark Knight" │ 2008 │   9    │ Action │  true   │
│    1    │   "The Matrix"    │ 1999 │  8.7   │ Action │  true   │
└---------┴-------------------┴------┴--------┴--------┴---------┘

That is the entire onboarding process.


3. Quick-Reference Cheat Sheet

Task Example Notes
Exact match title == "Inception" Case-sensitive
Case-insensitive title i== "inception" Prefix operator with i
Contains substring title *= "cep"
Starts with title ^= "The"
Ends with title $= "Knight"
Regex title ~= "In.*ion" Standard JS regex
Greater or equal rating >= 8.5 Works on numbers or dates
Less or equal rating <= 8.5
Boolean shorthand watched Same as watched == true
Negate !watched Same as watched == false
Grouping `(genre == Action
Match every row * Useful when you only need operations
Sort SORT rating desc asc is default
Limit LIMIT 10 Returns first 10 rows

4. Real-World Example: A Universal CLI Filter

Imagine you are building a command-line tool that fetches a large JSON array and lets users slice it however they like.

User types:

movie-cli '(genre == Action || genre == Comedy) && year >= 2000 && rating >= 8.5'

Core code (10 lines):

// movie-cli.ts
import { FilterQL } from "filterql"

const data = await (await fetch("https://api.example.com/movies")).json()
const schema = {
  title:  { type: "string", alias: "t" },
  year:   { type: "number", alias: "y" },
  rating: { type: "number" },
  genre:  { type: "string" },
  watched:{ type: "boolean", alias: "w" }
}

const query = process.argv[2] || "*"
const fql = new FilterQL({ schema })
console.table(fql.filter(data, query))

No extra flags, no DSL to learn—just plain FilterQL syntax.


5. Deep Dive in Plain Code

5.1 Declaring a Schema

const schema = {
  title:  { type: "string", alias: "t" },
  year:   { type: "number", alias: "y" },
  rating: { type: "number" },
  genre:  { type: "string" },
  watched:{ type: "boolean" }
}
  • type must be "string", "number", or "boolean".
  • alias is optional but shortens queries (y >= 2000).
  • Extra keys in the data are ignored—FilterQL only cares about what you declare.

5.2 Writing Queries

  1. Simple
    "rating >= 8.5"

  2. Combined
    (genre == Action || genre == Comedy) && year >= 2000

  3. With alias
    y >= 2000 && r >= 8.5

  4. Boolean fields
    watched && !archived

  5. Empty checks
    rating == "" → rows where rating is null, undefined, or empty string
    rating != "" → rows with an actual rating

  6. Match-all
    * → returns every row, useful when you only want operations.

5.3 Chaining Operations

Syntax:
filter-expression | operation1 [arg] | operation2 [arg]

Built-in operations:

  • SORT field [asc|desc]
    SORT rating desc
  • LIMIT number
    LIMIT 20

You can chain them:
year >= 2000 | SORT rating desc | LIMIT 10


6. Frequently Asked Questions

Question Answer
Is it case-sensitive? Yes by default. Prefix any operator with i for case-insensitive comparison.
What if my value contains spaces? Wrap it in double quotes: title == "The Dark Knight"
What if my value contains a double quote? Escape it with backslash: "A title with \"quotes\""
Can I filter nested objects? Current release supports one level only. Flatten or post-process as needed.
What happens on syntax errors? FilterQL throws a clear exception pointing to the exact character.
Does it have TypeScript types? Yes, the package ships with full .d.ts definitions.

7. Advanced: Rolling Your Own ROUND or GROUP Operation

Sometimes SORT and LIMIT are not enough. Suppose you want to round every rating to the nearest integer.

Step 1: Write the Function

import type { OperationMap } from "filterql"

const customOperations: OperationMap = {
  ROUND: (data, args, { resolveField }) => {
    const field = resolveField(args[0])
    if (!field) throw new Error(`Unknown field '${args[0]}'`)
    return data.map(item => ({
      ...item,
      [field]: Math.round(item[field] as number)
    }))
  }
}

Step 2: Register It

const fql = new FilterQL({ schema, customOperations })

Step 3: Use It

const rounded = fql.filter(movies, "* | SORT rating desc | ROUND rating")

The same pattern works for GROUP, UNIQUE, SUM, or any other transformation you need.


8. Language Specification in One Screen

If you plan to re-implement FilterQL in another language, these five bullets are the entire contract.

  1. Query shape
    filter | operation1 | operation2 ...

  2. Precedence (high → low)
    ()!&&||

  3. Comparison operators
    == != *= ^= $= ~= >= <=
    Prefix any with i for case-insensitive match.

  4. Values

    • Bare words allowed unless they contain spaces or special characters.
    • Wrap in " if needed.
    • Escape inner " with \".
  5. Operations

    • Upper-case names.
    • Zero or more space-separated arguments.

The canonical EBNF grammar is 38 lines long and can be copied verbatim from the repository.


9. Recap & Next Steps

In the time it takes to finish a cup of coffee, you have learned how to:

  • Install FilterQL with one command.
  • Declare a schema that tells the engine which fields exist.
  • Filter, sort, and limit any array of plain objects with a single readable string.
  • Extend the engine with your own transformations in fewer than 20 lines of code.

Ideas for your next project:

  • Build a browser bookmarklet that lets users filter any HTML table on a page.
  • Add FilterQL as the query language in your serverless API gateway.
  • Publish a Python port and share it with the community.

FilterQL will stay tiny, readable, and unopinionated—exactly the qualities that make it timeless.

See you at the next commit.