Agora
Definitions

Request Input

The input layer — parseSpatieRequest for the full Spatie/JSON:API shape (includes, sparse fieldsets, cursor pagination), resolveInputFromRequest for where input is read from, and normalizeInput for key casing.

parseFilterRequest covers the common case. Three additional, composable helpers cover the richer Spatie / JSON:API surface, control where input is sourced, and normalize field-name casing. All are pure reshapes — no validation or allow-listing (that happens downstream in applyFilter).

parseSpatieRequest — the full Spatie/JSON:API shape

A superset of parseFilterRequest: it understands the same filter / sort / search / distinct shapes plus JSON:API relation includes, sparse fieldsets, and cursor pagination. It returns a SpatieInput (a FilterInput extended with select, include, and cursor params) — ready to hand to applyFilter (offset) or applyCursor (keyset).

include and select are parsed but never applied. No runner reads them, because which relations and columns a client may reach is a policy decision this library deliberately leaves to you. Treat them as a request you validate and then act on with Lucid's preload / select yourself.

import { parseSpatieRequest } from '@adonis-agora/filter'

const input = parseSpatieRequest(request.qs())
Query stringMapped to
filter[name]=Alequals column filter
filter[id]=1,2,3 / filter[id][]=1&filter[id][]=2in column filter
filter[age][gte]=18operator column filter
sort=-createdAt,namesort items
distinct=city,tier / distinct[]=city&distinct[]=tierdistinct: ['city', 'tier']
include=posts,commentsinclude: ['posts', 'comments'] (parsed only — you apply the preload)
fields[users]=id,nameselect: ['id', 'name'] (sparse fieldsets, flattened + de-duped — parsed only)
page[number]=2&page[size]=10offset (page/size)
page[after]=<cursor>&page[size]=10cursor (after/first)

page[after] / page[before] route to cursor pagination (after/first, before/last); otherwise page[number] / page[size] route to offset. after wins if both cursor bounds are present.

resolveInputFromRequest — where input comes from

Reads the raw structured input from a request according to an InputSource — useful when filters arrive in the body (a complex POST /search) rather than the query string:

import { resolveInputFromRequest } from '@adonis-agora/filter'

const raw = resolveInputFromRequest(req, 'auto')
sourceBehavior
'auto'query on reads (GET/HEAD); query + body merged, body wins, on writes (POST/PUT/PATCH/DELETE)
'query' / 'body'always that container
'body.filters' (a dot-path)the nested object at that path
(req) => … (a function)a custom extractor

The request is treated structurally ({ method, query, body }), so an AdonisJS HttpContext.request or a plain object both work. The result is always a fresh shallow copy.

normalizeInput — key casing

Normalizes an input object's top-level keys — case-transforming them and optionally stripping Id suffixes and empty values — so a snake_case API can map onto camelCase model columns (or vice-versa):

import { normalizeInput } from '@adonis-agora/filter'

normalizeInput({ company_id: 5, first_name: 'a' }, { normalizer: 'camelCase' })
// → { companyId: 5, firstName: 'a' }

normalizeInput({ companyId: 5 }, { normalizer: 'snakeCase', dropId: true })
// → { company: 5 }   (snake_case + trailing Id stripped)
  • normalizer'camelCase', 'snakeCase', or a (key) => string function.
  • dropId — strip a trailing Id/_id (and drop a bare id). Default false.
  • stripEmpty — drop null/undefined/'' values. Default true.

Only top-level keys are normalized; nested values are left untouched. Prototype-pollution keys are dropped and the result has a null prototype, so a hostile key can never reach Object.prototype.

On this page