Interfaces
Cosa contengono
Il file src/domain/interfaces/ raccoglie tutti i contratti del layer dominio. Implementare una di queste interfacce è sufficiente per integrarsi col resto della libreria senza modificare codice esistente — è il punto di estensione canonico per:
- Strategie auth/cache custom
- Mock per testing
- Logger personalizzati
- HTTP client alternativi
import type {
IAuthHandler,
IAuthService,
IAuthStrategy,
ICacheService,
ICacheStrategy,
ICrudRepository,
IHttpClient,
ILogger,
AuthError,
PaginatedResponse,
RecordDataType,
} from '@pzeta/postgrest'
Tabella riepilogativa
| Interface | Ruolo | Implementazioni built-in |
|---|---|---|
IAuthHandler | Gestore auth lato HTTP client (token + refresh + 401 handling) | AuthService |
IAuthService | Operazioni auth applicative (login, register, refresh, ...) | AuthService |
IAuthStrategy | Persistenza e applicazione credenziali (Strategy Pattern) | JwtAuthStrategy, CookieAuthStrategy, ApiKeyAuthStrategy, NoAuthStrategy |
ICacheService | Servizio di caching con stale-while-revalidate | CacheService |
ICacheStrategy | Storage cache (Strategy Pattern) | IndexedDBCacheStrategy, LocalStorageCacheStrategy, SessionStorageCacheStrategy, MemoryCacheStrategy, NoneCacheStrategy |
ICrudRepository | Operazioni CRUD type-safe | PostgRESTRepository, EnhancedPostgRESTRepository |
IHttpClient | Astrazione HTTP (request, get, post, put, patch, delete) | FetchHttpClient, AxiosHttpClient |
ILogger | Logging strutturato (debug/warn/error) | ConsoleLogger, NullLogger |
IAuthHandler
Gestore auth utilizzato dall'HTTP client per aggiungere il token, tentare il refresh e gestire 401.
interface IAuthHandler {
getToken(): string | null
refreshToken(): Promise<AuthResponse | null>
handleAuthError(error: AxiosError): Promise<boolean>
}
| Metodo | Ruolo |
|---|---|
getToken() | Restituisce il token JWT corrente (o null) — chiamato prima di ogni richiesta |
refreshToken() | Rinnova il token usando il refresh token; null = utente deve ri-autenticarsi |
handleAuthError(error) | Decide se la richiesta originale va riprovata dopo il refresh |
handleAuthError deve verificare che l'errore non provenga dall'endpoint /auth/refresh stesso per evitare loop infiniti.IAuthService + AuthError
Operazioni di dominio per l'autenticazione.
interface AuthError {
code: string
message: string
timestamp: Date
details?: unknown
}
interface IAuthService {
login(credentials: UserCredentials): Promise<AuthResponse | null>
logout(refreshToken: string | null): Promise<void>
register(user: RegisterUser): Promise<CustomResponse>
activateAccount(token: string): Promise<CustomResponse>
forgotPassword(email: ForgotPassword): Promise<CustomResponse>
resetPassword(data: ResetPassword): Promise<CustomResponse>
refreshToken(token?: string, refreshToken?: string): Promise<AuthResponse | null>
getLastError(): AuthError | null
}
AuthError è la variante con timestamp usata per errori del flusso auth (vs ApiError generico).
IAuthStrategy + AuthStrategyType
Strategy Pattern per intercambiare meccanismi di autenticazione (JWT, Cookie, ApiKey, None) o implementarne di custom.
enum AuthStrategyType {
JWT = 'jwt',
COOKIE = 'cookie',
API_KEY = 'apiKey',
NONE = 'none',
}
interface IAuthStrategy {
readonly type: AuthStrategyType
applyAuth(config: AxiosRequestConfig): AxiosRequestConfig
isAuthenticated(): boolean
saveCredentials(authResponse: AuthResponse): void
clearCredentials(): void
getToken(): string | null
getRefreshToken(): string | null
supportsRefresh(): boolean
}
| Metodo | Ruolo |
|---|---|
type | Tipo immutabile (readonly) |
applyAuth(config) | Modifica la config Axios (header Authorization, withCredentials, ...) |
isAuthenticated() | Presenza credenziali (non verifica scadenza) |
saveCredentials(auth) | Persistenza dopo login/refresh riuscito |
clearCredentials() | Pulizia (logout) |
getToken() / getRefreshToken() | Access agli token correnti |
supportsRefresh() | true per JWT, false per ApiKey/None |
ICacheService
Servizio di caching HTTP con supporto background refresh (stale-while-revalidate).
interface ICacheService {
init(backgroundRefreshConfig?: Partial<BackgroundRefreshConfig>): void
setAuthHandler(handler: IAuthHandler): void
fetchWithCache<T>(
config: AxiosRequestConfig,
cacheKey?: string,
cacheTTL?: number,
skipCache?: boolean,
skipSave?: boolean,
): Promise<AxiosResponse<T>>
clearCache(key: string): void
invalidateAll(): void
cleanExpiredItems(): Promise<void>
}
Il metodo chiave è fetchWithCache<T> che combina lookup cache, fetch HTTP e save risultato. Vedi BackgroundRefreshConfig per il pattern stale-while-revalidate.
ICacheStrategy + CacheStrategyType
Strategy Pattern per lo storage cache. Tutte le operazioni sono asincrone per supportare backend come IndexedDB.
enum CacheStrategyType {
INDEXEDDB = 'indexeddb',
LOCALSTORAGE = 'localstorage',
SESSIONSTORAGE = 'sessionstorage',
MEMORY = 'memory',
NONE = 'none',
}
interface ICacheStrategy {
readonly type: CacheStrategyType
setItem<T>(key: string, value: T, ttl?: number): Promise<void>
getItem<T>(key: string): Promise<T | null>
removeItem(key: string): Promise<void>
clear(keyPrefix?: string): Promise<void>
hasItem(key: string): Promise<boolean>
getAllValues<T>(): Promise<T[]>
getAllKeys(): Promise<string[]>
cleanExpired(): Promise<number>
getStats(): Promise<{ size: number; keys: number; expired: number }>
supportsTTL(): boolean
isPersistent(): boolean
}
Confronto strategie
| Strategia | Capacità | Persistenza | Performance | Use case |
|---|---|---|---|---|
INDEXEDDB | ~50MB+ | Permanente | Ottima | PWA, offline-first, grandi dataset |
LOCALSTORAGE | ~5-10MB | Permanente | Buona (sync) | Config, preferenze utente |
SESSIONSTORAGE | ~5-10MB | Solo sessione | Buona (sync) | Cache per singola sessione |
MEMORY | RAM | Nessuna | Massima | Development, testing |
NONE | — | — | — | Disabilitazione cache (no-op) |
ICrudRepository + PaginatedResponse + RecordDataType
Operazioni CRUD type-safe con supporto a filtri PostgREST.
type RecordDataType =
| string | number | boolean | Date | null | undefined
| string[] | number[] | boolean[]
interface PaginatedResponse<T> {
data: T[]
pagination: {
totalCount?: number
hasMore: boolean
nextCursor?: string
}
}
interface ICrudRepository {
create<T extends Record<string, unknown>>(
path: string, element: Partial<T>, keyName?: keyof T,
): Promise<T | undefined>
read<T extends Record<string, unknown>, K = unknown>(
path: string, key: K, keyName: keyof T, forceRefresh?: boolean,
): Promise<T | undefined>
update<T extends Record<string, unknown>, K = unknown>(
path: string, key: K, element: Partial<T>, keyName: keyof T,
): Promise<T | undefined>
delete<T extends Record<string, unknown>, K = unknown>(
path: string, key: K, keyName: keyof T,
): Promise<T | undefined>
createMany<T extends Record<string, unknown>>(
path: string, elements: Partial<T>[], keyName?: keyof T,
): Promise<T[] | undefined>
readAll<T extends Record<string, unknown>>(
path: string, filters?: FilterCriteria<T>, forceRefresh?: boolean,
): Promise<T[] | PaginatedResponse<T> | undefined>
updateMany<T extends Record<string, unknown>>(
path: string, element: Partial<T>, filters?: FilterCriteria<T>, keyName?: keyof T,
): Promise<T[] | undefined>
deleteMany<T extends Record<string, unknown>, K = unknown>(
path: string, keys: K[], keyName: keyof T,
): Promise<T[] | undefined>
}
pagination.cursor o si richiede il count totale, il repository ritorna PaginatedResponse<T> con metadata. Altrimenti T[] semplice. Vedi EnhancedPostgRESTRepository per la logica di switch.IHttpClient
Astrazione HTTP — permette di intercambiare FetchHttpClient (nativo) e AxiosHttpClient (opzionale).
interface IHttpClient {
request<T = unknown>(config: AxiosRequestConfig): Promise<AxiosResponse<T>>
get<T = unknown>(url: string, config?: AxiosRequestConfig): Promise<AxiosResponse<T>>
post<T = unknown>(url: string, data?: unknown, config?: AxiosRequestConfig): Promise<AxiosResponse<T>>
put<T = unknown>(url: string, data?: unknown, config?: AxiosRequestConfig): Promise<AxiosResponse<T>>
patch<T = unknown>(url: string, data?: unknown, config?: AxiosRequestConfig): Promise<AxiosResponse<T>>
delete<T = unknown>(url: string, config?: AxiosRequestConfig): Promise<AxiosResponse<T>>
setAuthHandler(handler: IAuthHandler): void
getAxiosInstance(): AxiosInstance
}
I tipi di richiesta/risposta riusano AxiosRequestConfig/AxiosResponse per uniformità anche nel client fetch (che li wrappa internamente).
ILogger
Logger iniettabile (alternativa a console.* diretto). I consumer possono passare un logger custom (es. Pino, Winston, browser logger) per controllare verbosità e destinazione.
interface ILogger {
debug(message: string, ...args: unknown[]): void
warn(message: string, ...args: unknown[]): void
error(message: string, ...args: unknown[]): void
}
@since 0.2.0 — ILogger è disponibile dalla v0.2.0. Built-in: ConsoleLogger (delega a console.*) e NullLogger (silenzioso, per produzione/test).Estensione — esempio strategia custom
import { AuthStrategyType, type IAuthStrategy, type AuthResponse } from '@pzeta/postgrest'
import type { AxiosRequestConfig } from 'axios'
class BearerHeaderStrategy implements IAuthStrategy {
readonly type = AuthStrategyType.JWT
private token: string | null = null
applyAuth(config: AxiosRequestConfig): AxiosRequestConfig {
if (this.token) {
config.headers = { ...config.headers, Authorization: `Bearer ${this.token}` }
}
return config
}
isAuthenticated(): boolean { return this.token !== null }
saveCredentials(auth: AuthResponse): void { this.token = auth.token }
clearCredentials(): void { this.token = null }
getToken(): string | null { return this.token }
getRefreshToken(): string | null { return null }
supportsRefresh(): boolean { return false }
}
// Usa la strategia custom
const client = new PostgRESTClient({
apiUrl: 'https://api.example.com',
customAuthStrategy: new BearerHeaderStrategy(),
})
await client.init()
Tipi correlati
AuthEntity— DTO usati dalle interfacce authFilterCriteria— input diICrudRepository.readAll/updateManyCacheMetadata— value objects usati daICacheService/ICacheStrategyApiError— struttura errore PostgREST
FilterCriteria
Tipo principale per query PostgREST type-safe — combina filtri sui campi con metadati di query (operatori, OR conditions, sort, paginazione, vertical filtering).
HTTP
Client HTTP duale (Fetch API e Axios), factory di creazione, gestione CORS e wrapper strategy-aware per @pzeta/postgrest.