A Go library for parsing and validating the query expressions commonly used in REST APIs and database queries — fields selection, filtering, and sorting. Each parser is driven by an allow-list of fields, so only fields you permit can appear in a query, helping secure your endpoints against invalid or malicious input.
go get github.com/slashdevops/qfv@latestUpdate to the latest release with go get -u github.com/slashdevops/qfv@latest.
package main
import (
"fmt"
"log"
qfv "github.com/slashdevops/qfv"
)
func main() {
allowedFields := []string{"first_name", "last_name", "email", "created_at"}
// Parsers are safe to build once and reuse across goroutines.
filterParser := qfv.NewFilterParser(allowedFields)
sortParser := qfv.NewSortParser(allowedFields)
fieldsParser := qfv.NewFieldsParser(allowedFields)
node, err := filterParser.Parse("first_name = 'John' AND created_at > '2023-01-01'")
if err != nil {
log.Fatal(err)
}
fmt.Println(node.String()) // ((first_name = 'John') AND (created_at > '2023-01-01'))
if _, err := sortParser.Parse("first_name ASC, created_at DESC"); err != nil {
log.Fatal(err)
}
if _, err := fieldsParser.Parse("first_name, last_name, email"); err != nil {
log.Fatal(err)
}
}- Three validators — fields selection, filtering, and sorting, each gated by
an allow-list of fields (nested dot-notation like
user.profile.agesupported). - Rich, PostgreSQL-flavored filter grammar — comparison, logical,
IN,BETWEEN [SYMMETRIC],LIKE/ILIKE,SIMILAR TO, POSIX regex,IS NULL,IS [NOT] TRUE/FALSE/UNKNOWN, andIS [NOT] DISTINCT FROM. - Configurable — restrict which operators or sort directions are allowed.
- Returns an AST you can walk or render, with precise, aggregated errors.
- Concurrency-safe parsers.
Every parser is gated by the same allow-list of fields. Untrusted query-string parameters go in; validated, structured values come out — an AST for filters, typed lists for fields and sorting — so nothing outside your allow-list ever reaches your data layer.
flowchart LR
Q["HTTP query string<br/>?fields=…&filter=…&sort=…"]
Q --> FP["FieldsParser"]
Q --> FI["FilterParser"]
Q --> SP["SortParser"]
AL(["Allow-list of fields"]) -. gates .-> FP
AL -. gates .-> FI
AL -. gates .-> SP
FP --> FN["FieldsNode<br/>[]string"]
FI --> AST["Filter AST<br/>(Node tree)"]
SP --> SN["SortNode<br/>[]SortFieldNode"]
FN --> APP["Your handler / SQL builder"]
AST --> APP
SN --> APP
APP --> DB[("Database")]
The filter parser is a hand-written recursive-descent parser: a Lexer
tokenizes the input, then the parser builds the AST while honoring operator
precedence (OR < AND < comparison) and the allow-list.
flowchart LR
IN["input string"] --> LEX["Lexer<br/>tokenize"]
LEX --> TOK["[]Token"]
TOK --> PAR["FilterParser<br/>recursive descent"]
PAR --> AST["AST root (Node)"]
PAR -. "collects every error" .-> ERR["errors.Join(…)"]
Full documentation lives in docs/:
- Getting Started — install, update, complete example
- Filtering — the full operator/predicate reference
- Configuration — restrict operators and sort directions
- Error Handling — inspecting validation failures
- Migration Guide — upgrading between versions
API reference and runnable examples are on pkg.go.dev.
See the repository for license details.