Documentation
¶
Overview ¶
Package hush parses, builds, and validates structured query strings from URL parameters.
Overview ¶
hush processes URL query strings through a three-stage pipeline:
Parse → Build → Validate → *Query
Given a URL query string like:
filters[title][$containsi]=go&sort[0]=createdAt:desc&fields[0]=title
hush produces a typed Query value with structured Filters, Fields, Sort, Pagination, and Populates — validated against a user-defined Schema.
Entrypoints ¶
The two main entrypoints are:
- NewSchema — creates a SchemaBuilder for defining which fields, operators, relations, and limits are allowed. Call SchemaBuilder.Build to produce a Schema.
- Parse — takes net/url.Values and a Schema, runs the full pipeline, and returns a validated *Query.
Schema Definition ¶
Use NewSchema and the builder methods to declare what is allowed:
schema, err := hush.NewSchema("article").
Filterable("title", hush.TypeString, hush.OpEq, hush.OpContainsi).
Filterable("views", hush.TypeNumber, hush.OpGt).
Sortable("title", "createdAt").
Selectable("title", "body", "createdAt").
Relation("author", authorSchema, 5).
MaxLimit(100).
Build()
Query String Syntax ¶
The expected query string format uses bracket notation:
filters[field][$operator]=value — filter conditions filters[$and][0][field][$operator]=value — AND combinators filters[$or][0][field][$operator]=value — OR combinators filters[$not][field][$operator]=value — NOT combinator fields[0]=fieldName — field selection sort[0]=fieldName:desc — sorting (asc or desc) groupBy[0]=fieldName — group by field aggregations[name][func]=count — aggregate function (count, sum, avg) aggregations[name][field]=fieldName — field to aggregate on pagination[start]=0 — offset pagination[limit]=25 — page size pagination[withCount]=true — include total row count (default true) populate[0]=relationName — include relations populate[relation][fields][0]=name — nested field selection populate=* — populate all relations
Error Handling ¶
Parse and build errors fail fast (first error). Validation errors accumulate across all query sections and are returned together via errors.Join.
All errors can be inspected with errors.Is against the exported sentinel values (e.g. ErrUnknownField, ErrOperatorNotAllowed). Structured error context is available via errors.As with the *Error type.
Index ¶
- Constants
- Variables
- func Coerce(t FieldType, raw string) (any, error)
- func EscapeLike(s string) string
- func IsNotNullOperator(op Operator) bool
- func IsNullOperator(op Operator) bool
- type Aggregation
- type And
- type Condition
- type Error
- type Field
- type FieldDef
- type FieldType
- type Filter
- type Not
- type Operator
- type Or
- type Pagination
- type Path
- type Populate
- type Query
- type RelationDef
- type RelationOption
- type Schema
- func (s *Schema) Aggregatable(name string) bool
- func (s *Schema) Filterable(name string) (FieldDef, bool)
- func (s *Schema) Groupable(name string) bool
- func (s *Schema) Inner() *schema.Schema
- func (s *Schema) MaxLimit() int
- func (s *Schema) Name() string
- func (s *Schema) Relation(name string) (RelationDef, bool)
- func (s *Schema) Relations() map[string]RelationDef
- func (s *Schema) Selectable(name string) bool
- func (s *Schema) SelectableFields() []string
- func (s *Schema) Sortable(name string) bool
- type SchemaBuilder
- func (b *SchemaBuilder) Aggregatable(names ...string) *SchemaBuilder
- func (b *SchemaBuilder) Build() (*Schema, error)
- func (b *SchemaBuilder) Filterable(name string, t FieldType, ops ...Operator) *SchemaBuilder
- func (b *SchemaBuilder) Groupable(names ...string) *SchemaBuilder
- func (b *SchemaBuilder) MaxLimit(n int) *SchemaBuilder
- func (b *SchemaBuilder) Relation(name string, target *Schema, maxDepth int, opts ...RelationOption) *SchemaBuilder
- func (b *SchemaBuilder) Selectable(names ...string) *SchemaBuilder
- func (b *SchemaBuilder) Sortable(names ...string) *SchemaBuilder
- type Sort
- type SortDirection
- type Value
Examples ¶
Constants ¶
const ( OpEq = query.OpEq OpNe = query.OpNe OpGt = query.OpGt OpGte = query.OpGte OpLt = query.OpLt OpLte = query.OpLte OpIn = query.OpIn OpNotIn = query.OpNotIn OpBetween = query.OpBetween OpContains = query.OpContains OpContainsi = query.OpContainsi OpStartsWith = query.OpStartsWith OpEndsWith = query.OpEndsWith OpNull = query.OpNull OpNotNull = query.OpNotNull )
Comparison operators.
const ( SortAsc = query.SortAsc SortDesc = query.SortDesc )
Sort directions.
const ( TypeString = schema.TypeString TypeNumber = schema.TypeNumber TypeBool = schema.TypeBool TypeDate = schema.TypeDate )
const AbsoluteMaxLimit = schema.AbsoluteMaxLimit
AbsoluteMaxLimit is the hard upper bound for pagination limits.
const DefaultMaxLimit = schema.DefaultMaxLimit
DefaultMaxLimit is the default maximum pagination limit.
Variables ¶
var ( ErrEmptyKey = parse.ErrEmptyKey ErrEmptySegment = parse.ErrEmptySegment ErrInvalidSyntax = parse.ErrInvalidSyntax ErrUnexpectedCharacter = parse.ErrUnexpectedCharacter )
Parse phase errors.
var ( ErrInvalidFields = build.ErrInvalidFields ErrInvalidSort = build.ErrInvalidSort ErrInvalidFilters = build.ErrInvalidFilters ErrInvalidGroupBy = build.ErrInvalidGroupBy )
Build phase errors.
var ( ErrEmptyName = schema.ErrEmptyName ErrNoOperators = schema.ErrNoOperators ErrUnknownOperator = schema.ErrUnknownOperator ErrDuplicateField = schema.ErrDuplicateField ErrDuplicateRelation = schema.ErrDuplicateRelation ErrNilTarget = schema.ErrNilTarget ErrInvalidMaxDepth = schema.ErrInvalidMaxDepth ErrInvalidMaxLimit = schema.ErrInvalidMaxLimit ErrUnknownFieldType = schema.ErrUnknownFieldType )
Schema builder errors.
var ( ErrInvalidPath = validate.ErrInvalidPath ErrUnknownField = validate.ErrUnknownField ErrOperatorNotAllowed = validate.ErrOperatorNotAllowed ErrNestingTooDeep = validate.ErrNestingTooDeep ErrInvalidValue = validate.ErrInvalidValue ErrUnknownFilterNode = validate.ErrUnknownFilterNode ErrMissingSchema = validate.ErrMissingSchema ErrUnknownGroupBy = validate.ErrUnknownGroupBy )
Validation phase errors.
var ( ErrInvalidPopulate = query.ErrInvalidPopulate ErrInvalidPagination = query.ErrInvalidPagination ErrInvalidAggregation = query.ErrInvalidAggregation )
Shared cross-domain errors.
Functions ¶
func Coerce ¶
Coerce parses a raw filter value string according to the given field type, returning a string for TypeString, a float64 for TypeNumber, a bool for TypeBool, and a time.Time for TypeDate. The parsed Query already carries these coerced values on each Condition.Values, so most adapters never need to call this directly.
func EscapeLike ¶
EscapeLike escapes LIKE wildcard characters in a literal value so that user input is matched literally rather than interpreted as a pattern. It escapes the backslash, percent, and underscore characters using a backslash escape.
Adapters must wrap the escaped value with the appropriate '%' placeholders for the pattern operators:
$contains: "%" + EscapeLike(v) + "%" $startsWith: EscapeLike(v) + "%" $endsWith: "%" + EscapeLike(v)
func IsNotNullOperator ¶
IsNotNullOperator reports whether the operator is $notNull, which maps to SQL's IS NOT NULL. NULL operators carry no coerced values (Condition.Values is nil).
func IsNullOperator ¶
IsNullOperator reports whether the operator is $null, which maps to SQL's IS NULL. NULL operators carry no coerced values (Condition.Values is nil).
Types ¶
type Aggregation ¶
type Aggregation = query.Aggregation
Aggregation specifies a computed aggregate function with an output alias.
type Query ¶
Query is the root result of parsing and validating query parameters.
func Parse ¶
Parse processes URL query values through the three-stage pipeline (parse → build → validate) and returns a validated Query.
Example ¶
package main
import (
"fmt"
"net/url"
"github.com/DhimasYulian/hush"
)
func main() {
schema, err := hush.NewSchema("article").
Filterable("title", hush.TypeString, hush.OpEq, hush.OpContainsi).
Sortable("createdAt").
Selectable("title", "body").
MaxLimit(100).
Build()
if err != nil {
panic(err)
}
values := url.Values{
"filters[title][$containsi]": {"go"},
"sort[0]": {"createdAt:desc"},
"fields[0]": {"title"},
"pagination[limit]": {"25"},
}
query, err := hush.Parse(values, schema)
if err != nil {
fmt.Println("error:", err)
return
}
fmt.Println("filter field:", query.Filters.(hush.Condition).Path[0])
fmt.Println("sort dir:", query.Sort[0].Direction)
fmt.Println("fields:", query.Fields)
fmt.Println("limit:", *query.Pagination.Limit)
}
Output: filter field: title sort dir: desc fields: [title] limit: 25
type RelationDef ¶
type RelationDef = schema.RelationDef
RelationDef describes a named relation to another schema with a max nesting depth.
type RelationOption ¶
type RelationOption func(*RelationDef)
RelationOption configures a relation declaration.
func Hidden ¶
func Hidden() RelationOption
Hidden marks a relation as hidden from wildcard populate (populate=*).
type Schema ¶
type Schema struct {
// contains filtered or unexported fields
}
Schema defines the allowed query operations for a resource. Built via SchemaBuilder.
func (*Schema) Aggregatable ¶
Aggregatable reports whether the field can be used in aggregations.
func (*Schema) Filterable ¶
Filterable returns the field definition if the field is filterable.
func (*Schema) Inner ¶
Inner returns the underlying schema definition. It is exposed for integrations (such as the hush/gorm adapter) that need to walk relations and their nested targets directly.
func (*Schema) Relation ¶
func (s *Schema) Relation(name string) (RelationDef, bool)
Relation returns the named relation definition if it exists.
func (*Schema) Relations ¶
func (s *Schema) Relations() map[string]RelationDef
Relations returns a copy of all declared relations.
func (*Schema) Selectable ¶
Selectable reports whether the field can be selected.
func (*Schema) SelectableFields ¶
SelectableFields returns all selectable field names in sorted order.
type SchemaBuilder ¶
type SchemaBuilder struct {
// contains filtered or unexported fields
}
SchemaBuilder constructs a Schema with accumulated validation errors. Call Build to finalize and check for errors.
func NewSchema ¶
func NewSchema(name string) *SchemaBuilder
NewSchema creates a new SchemaBuilder with the given resource name.
Example ¶
package main
import (
"fmt"
"github.com/DhimasYulian/hush"
)
func main() {
schema, err := hush.NewSchema("article").
Filterable("title", hush.TypeString, hush.OpEq, hush.OpContainsi).
Filterable("views", hush.TypeNumber, hush.OpGt, hush.OpLt).
Sortable("title", "createdAt").
Selectable("title", "body", "createdAt").
MaxLimit(100).
Build()
if err != nil {
panic(err)
}
fmt.Println("name:", schema.Name())
fmt.Println("max limit:", schema.MaxLimit())
}
Output: name: article max limit: 100
func (*SchemaBuilder) Aggregatable ¶
func (b *SchemaBuilder) Aggregatable(names ...string) *SchemaBuilder
Aggregatable declares fields as aggregatable (available for aggregations).
func (*SchemaBuilder) Build ¶
func (b *SchemaBuilder) Build() (*Schema, error)
Build finalizes the schema and returns any accumulated validation errors.
func (*SchemaBuilder) Filterable ¶
func (b *SchemaBuilder) Filterable(name string, t FieldType, ops ...Operator) *SchemaBuilder
Filterable declares a field as filterable with a type and set of allowed operators.
func (*SchemaBuilder) Groupable ¶
func (b *SchemaBuilder) Groupable(names ...string) *SchemaBuilder
Groupable declares fields as groupable (available for groupBy).
func (*SchemaBuilder) MaxLimit ¶
func (b *SchemaBuilder) MaxLimit(n int) *SchemaBuilder
MaxLimit sets the maximum allowed pagination limit. Must be between 1 and AbsoluteMaxLimit.
func (*SchemaBuilder) Relation ¶
func (b *SchemaBuilder) Relation(name string, target *Schema, maxDepth int, opts ...RelationOption) *SchemaBuilder
Relation declares a named relation to another schema with a max nesting depth.
func (*SchemaBuilder) Selectable ¶
func (b *SchemaBuilder) Selectable(names ...string) *SchemaBuilder
Selectable declares fields as selectable (available for field selection).
func (*SchemaBuilder) Sortable ¶
func (b *SchemaBuilder) Sortable(names ...string) *SchemaBuilder
Sortable declares fields as sortable.
type SortDirection ¶
type SortDirection = query.SortDirection
SortDirection is a string-typed sort direction ("asc" or "desc").
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
Package main demonstrates ALL hush features by walking a comprehensive query.
|
Package main demonstrates ALL hush features by walking a comprehensive query. |
|
Package gorm translates validated hush queries into GORM clauses so a consumer can integrate hush with gorm by importing hush/gorm and using db.Scopes without writing any per-operator code.
|
Package gorm translates validated hush queries into GORM clauses so a consumer can integrate hush with gorm by importing hush/gorm and using db.Scopes without writing any per-operator code. |
|
internal
|
|
|
coerce
Package coerce converts raw filter value strings to their schema-declared Go types so query adapters can bind values without re-parsing them.
|
Package coerce converts raw filter value strings to their schema-declared Go types so query adapters can bind values without re-parsing them. |
|
Package mongo translates validated hush queries into MongoDB bson documents and aggregation pipelines so a consumer can integrate hush with the official MongoDB Go driver without writing any per-operator code.
|
Package mongo translates validated hush queries into MongoDB bson documents and aggregation pipelines so a consumer can integrate hush with the official MongoDB Go driver without writing any per-operator code. |