FilterUtils
Cos'è
FilterUtils è una classe static-only (senza stato) che funge da ponte fra il modello tipato del package — FilterCriteria<T> — e la sintassi querystring nativa di PostgREST. Tutti i repository e i service della libreria la usano internamente; viene esposta pubblicamente per casi avanzati (build URL ad-hoc, paginazione custom, integrazione con MFE).
import { FilterUtils } from '@pzeta/postgrest'
API
| Metodo | Firma | Scopo |
|---|---|---|
buildQueryParams | <T>(filters?: FilterCriteria<T> | MetadataOnlyFilter<T>) => Record<string, string> | Converte un FilterCriteria in query params PostgREST (filtri AND, OR, sort, paginazione, select verticale) |
buildPaginationHeaders | (preferCount = false) => Record<string, string> | Costruisce Prefer: count=exact per ricevere il conteggio totale nell'header Content-Range |
extractPaginationInfo | (response: AxiosResponse) => { totalCount?, hasMore, nextCursor? } | Estrae informazioni di paginazione dagli headers Content-Range e next-range |
parseContentRange | (header?: string | null) => number | Parser dell'header Content-Range (0-9/42 → 42, 0-9/* → 0) |
hasPagination | <T>(filters?) => boolean | Verifica se i criteri includono limit, offset o cursor validi |
buildBulkDeleteParams | <T>(filters?: FilterCriteria<T>) => Record<string, string> | Query params per DELETE massiva (ignora sort/pagination/vertical) |
buildBulkUpdateParams | <T>(filters?: FilterCriteria<T>) => Record<string, string> | Alias di buildBulkDeleteParams per PATCH massivo |
buildBulkOperationHeaders | (returnRepresentation = false) => Record<string, string> | Headers per bulk POST/PATCH (Prefer: return=representation o return=minimal) |
buildQueryParams — il caso d'uso principale
Trasforma un FilterCriteria<T> in un oggetto Record<string, string> pronto per essere passato a URLSearchParams, Axios params, o Fetch URL:
import { FilterUtils } from '@pzeta/postgrest'
interface User {
iduser: number
name: string
status: 'active' | 'inactive'
createdAt: Date
}
const params = FilterUtils.buildQueryParams<User>({
name: { operator: 'ilike', value: '%mario%' },
status: { operator: 'eq', value: 'active' },
sort: { field: 'createdAt', direction: 'desc' },
pagination: { limit: 20, offset: 0 },
})
// Risultato:
// {
// name: 'ilike.%25mario%25',
// status: 'eq.active',
// order: 'createdAt.desc',
// limit: '20',
// offset: '0',
// }
// → URL finale: ?name=ilike.%25mario%25&status=eq.active&order=createdAt.desc&limit=20&offset=0
I valori sono URL-encoded automaticamente (encodeURIComponent); Date viene serializzato come ISO string; gli array per in.(...) e between sono gestiti nativamente.
Tabella operatori: FilterOperator → PostgREST syntax
FilterOperator | Valore esempio | PostgREST querystring | Note |
|---|---|---|---|
eq | 'active' | ?col=eq.active | Uguaglianza esatta |
neq | 'deleted' | ?col=neq.deleted | Diverso da |
gt | 100 | ?col=gt.100 | Maggiore di |
lt | 100 | ?col=lt.100 | Minore di |
gte | 18 | ?col=gte.18 | Maggiore o uguale |
lte | 5 | ?col=lte.5 | Minore o uguale |
like | '%john%' | ?col=like.%25john%25 | Pattern case-sensitive |
ilike | '%mario%' | ?col=ilike.%25mario%25 | Pattern case-insensitive |
in | ['a','b','c'] | ?col=in.(a,b,c) | Valore in insieme |
cs | '{ts}' | ?col=cs.%7Bts%7D | Array/range contains |
cd | '{ts,js}' | ?col=cd.%7Bts%2Cjs%7D | Array/range contained by |
between | [10, 100] | ?col=and=(gte.10,lte.100) | Range chiuso (composto AND) |
is | null | ?col=is.null | Confronto NULL |
not | null | ?col=not.null | NOT NULL |
fts | 'typescript' | ?col=fts.typescript | Full-text search |
plfts | 'ts prog' | ?col=plfts.ts%20prog | Plain-to-tsquery |
phfts | 'clean arch' | ?col=phfts.clean%20arch | Phrase-to-tsquery |
sl | '[1,10]' | ?col=sl.%5B1%2C10%5D | Strictly left of (range) |
sr | '[1,10]' | ?col=sr.%5B1%2C10%5D | Strictly right of (range) |
nxl | '[1,10]' | ?col=nxl.%5B1%2C10%5D | Does not extend to left |
nxr | '[1,10]' | ?col=nxr.%5B1%2C10%5D | Does not extend to right |
adj | '[1,10]' | ?col=adj.%5B1%2C10%5D | Adjacent to (range) |
like/ilike usa l'asterisco (*) come wildcard, mentre il valore SQL grezzo usa %. FilterUtils non sostituisce automaticamente: passa il pattern come lo vuoi sul server ('%mario%' se il backend accetta SQL standard, '*mario*' se l'API espone wildcard PostgREST).Filtri OR (orConditions)
Gli operatori si possono combinare in gruppi OR:
const params = FilterUtils.buildQueryParams<User>({
status: { operator: 'eq', value: 'active' },
orConditions: [{
or: [
{ field: 'role', condition: { operator: 'eq', value: 'admin' } },
{ field: 'role', condition: { operator: 'eq', value: 'moderator' } },
],
}],
})
// → ?status=eq.active&or=(role.eq.admin,role.eq.moderator)
Paginazione e Content-Range
PostgREST ritorna il conteggio totale solo se richiesto via header Prefer: count=exact:
const response = await axios.get('/users', {
params: FilterUtils.buildQueryParams({ pagination: { limit: 20 } }),
headers: FilterUtils.buildPaginationHeaders(true),
})
const info = FilterUtils.extractPaginationInfo(response)
// info.totalCount = 150 (da header Content-Range: "0-19/150")
// info.hasMore = true se presente header next-range
Per parsing manuale dell'header (es. da Fetch):
const total = FilterUtils.parseContentRange(response.headers.get('Content-Range'))
// "0-19/150" → 150
// "0-19/*" → 0
// null → 0
Bulk DELETE / PATCH
Per operazioni massive, le proprietà sort, pagination, vertical sono ignorate (non hanno senso semantico):
const params = FilterUtils.buildBulkDeleteParams<User>({
status: { operator: 'eq', value: 'inactive' },
lastLogin: { operator: 'lt', value: new Date('2024-01-01') },
})
// → ?status=eq.inactive&lastLogin=lt.2024-01-01T00%3A00%3A00.000Z
const headers = FilterUtils.buildBulkOperationHeaders(true)
// { 'Content-Type': 'application/json', 'Accept': 'application/json', 'Prefer': 'return=representation' }
await axios.patch('/users', { archived: true }, { params, headers })
Pattern: combinare con URLSearchParams
buildQueryParams ritorna un oggetto piatto: usalo con URLSearchParams per costruire URL manualmente:
const params = FilterUtils.buildQueryParams({ name: { operator: 'ilike', value: '%foo%' } })
const url = `/users?${new URLSearchParams(params).toString()}`
// → /users?name=ilike.%25foo%25
buildQueryParams ritorna valori già URL-encoded. Non passare a encodeURIComponent nuovamente: causa double-encoding.Tipi correlati
FilterCriteria— input principaleFilterConditionFilterOperatorBasePostgrestService— usa internamenteFilterUtils
ConsoleLogger
Logger di default basato su console.* — implementazione standard di ILogger usata dal package quando non viene iniettato un logger custom.
NullLogger
Logger no-op che scarta silenziosamente tutti i messaggi — usato per disabilitare completamente il logging della libreria in produzione o nei test.