RepositoryUtils
Overview
RepositoryUtils è una classe statica che centralizza la logica comune ai repository: gestione errori HTTP, normalizzazione path, hash deterministici dei filtri per cache key. Include la classe RestError — l'errore tipizzato che ogni repository PostgREST lancia in caso di fallimento.
import { RepositoryUtils, RestError } from '@pzeta/postgrest'
import type { ApiError } from '@pzeta/postgrest'
RestError
Errore Error-subclass arricchito con i metadati strutturati di PostgREST. Tutti i metodi CRUD/RPC lo lanciano quando la richiesta HTTP fallisce.
export class RestError extends Error {
status: number // HTTP status (400, 404, 409, 500...)
code: string // PostgreSQL/PostgREST error code o 'HTTP_ERROR'
details: string | null // Dettagli aggiuntivi (es. constraint violato)
hint: string | null // Suggerimento per la risoluzione
}
| Campo | Tipo | Esempio |
|---|---|---|
name | 'RestError' | — |
message | string | 'duplicate key value violates unique constraint' |
status | number | 409 |
code | string | '23505' (Postgres) / 'PGRST116' (PostgREST) / 'HTTP_ERROR' |
details | string | null | 'Key (email)=(mario@example.com) already exists.' |
hint | string | null | 'Use ON CONFLICT or check uniqueness before insert' |
Try/catch tipizzato
import { RestError } from '@pzeta/postgrest'
try {
await userRepo.create({ email: 'mario@example.com', ... })
} catch (error) {
if (error instanceof RestError) {
switch (error.code) {
case '23505':
notify.error(`Email già registrata: ${error.details}`)
break
case '23503':
notify.error('Riferimento non valido — controlla i campi collegati')
break
case '42501':
notify.error('Permesso negato per questa operazione')
break
default:
notify.error(`Errore ${error.status}: ${error.message}`)
if (error.hint) console.info('Hint:', error.hint)
}
} else {
notify.error('Errore di rete — riprova')
}
}
Codici errore comuni
Tabella di riferimento dei code più frequenti restituiti da PostgREST/PostgreSQL:
| Code | Status HTTP | Significato | Causa tipica |
|---|---|---|---|
23505 | 409 | Unique violation | Inserimento duplicato su colonna UNIQUE |
23503 | 409 | Foreign key violation | FK puntata a record inesistente o cancellazione di parent con figli |
23502 | 400 | Not null violation | Campo obbligatorio mancante |
23514 | 400 | Check constraint violation | Valore non soddisfa CHECK |
42501 | 403 | Insufficient privilege | Permesso negato (RLS / GRANT) |
42P01 | 404 | Undefined table | Tabella/vista inesistente o schema sbagliato |
42703 | 400 | Undefined column | Colonna inesistente nel select= |
PGRST116 | 406 | No rows found | Accept: vnd.pgrst.object+json con 0 risultati |
PGRST301 | 401 | JWT expired/invalid | Token scaduto o malformato |
PGRST302 | 401 | Anonymous role disabled | Richiesta senza JWT su endpoint privato |
HTTP_ERROR | qualsiasi | Errore HTTP senza struttura ApiError | Proxy / network / 5xx generici |
code in messaggi italiani user-facing centralizzati. Usa error.details per il debug developer (console) e error.message come fallback UI.API RepositoryUtils
handleError(err): never
Trasforma un errore Axios in RestError tipizzato. Lancia sempre — usato internamente da ogni metodo CRUD di PostgRESTRepository.
try {
await axios.get('/api/users')
} catch (error) {
RepositoryUtils.handleError(error) // throws RestError
}
Logica interna:
TypeGuards.hasHttpResponse(err)→ estraestatusedata- Se
datahacode+message→ lanciaRestErrorstrutturato - Altrimenti → lancia
RestErrorconcode: 'HTTP_ERROR' - Errori di rete senza response →
throw new Error('Errore di rete imprevisto')
parsePath(path): [basePath, queryArgs]
Separa path base e query string:
const [base, args] = RepositoryUtils.parsePath('/utenti?select=idutente,nome')
// base => '/utenti'
// args => 'select=idutente,nome'
buildFullPath(path, queryArgs?, newParams?): string
Combina path base con parametri esistenti e nuovi (merge con sovrascrittura su chiavi duplicate):
RepositoryUtils.buildFullPath('/utenti', 'select=idutente,nome', {
status: 'eq.active',
})
// => '/utenti?select=idutente%2Cnome&status=eq.active'
generateFilterHash(filters?): string
Genera un hash deterministico compatto per usare i filtri come cache key. Le chiavi sono ordinate alfabeticamente, pagination e sort ricevono formato dedicato.
RepositoryUtils.generateFilterHash({
name: { operator: 'eq', value: 'Mario' },
pagination: { limit: 20, offset: 0 },
})
// => 'name[operator:eq,value:Mario]|p[l:20]'
assertDefined<T>(value, message): asserts value is T
Assertion function per narrowing TypeScript:
RepositoryUtils.assertDefined(user, 'Utente non trovato')
// Da qui TypeScript sa che user: User (non undefined)
console.log(user.nome)
Metodi deprecati
| Metodo | Stato | Sostituto |
|---|---|---|
generateCacheKey(operation, path, key?) | @deprecated | Usa template literal ${operation}/${path}/${key} |
buildHeaders(options) | @deprecated | Costruzione inline negli specifici metodi |
ApiError
Re-export type dalla domain layer — rappresenta lo shape standard di una risposta errore PostgREST nel body JSON:
interface ApiError {
code: string
message: string
details?: string | null
hint?: string | null
}
RepositoryUtils.handleError() la valida runtime via TypeGuards.hasProperties prima di costruire un RestError.
Cross-reference
TypeGuards— validazione runtime usata dahandleErrorPostgRESTRepository— usaRepositoryUtilsinternamente in tutti i metodiCacheService— consuma le cache key generate dagenerateFilterHashFilterCriteria— input digenerateFilterHash
RepositoryFactory
Factory statica per creare EnhancedPostgRESTRepository con preset enterprise — JWT, Cookie, Cached 24h, Realtime, Public — applicando DEFAULT_REPOSITORY_CONFIG.
TypeGuards
Type guards runtime per validazione type-safe — narrowing TypeScript su unknown, gestione difensiva errori Axios e validazione struttura oggetti.