Types

MFEHttpClient

Client HTTP fornito dalla shell con autenticazione e refresh token gestiti automaticamente. Include MFEHttpRequestOptions e MFEHttpResponse.

MFEHttpClient

interface MFEHttpClient {
  get<T = unknown>(url: string, options?: MFEHttpRequestOptions): Promise<MFEHttpResponse<T>>
  post<T = unknown>(url: string, data?: unknown, options?: MFEHttpRequestOptions): Promise<MFEHttpResponse<T>>
  put<T = unknown>(url: string, data?: unknown, options?: MFEHttpRequestOptions): Promise<MFEHttpResponse<T>>
  patch<T = unknown>(url: string, data?: unknown, options?: MFEHttpRequestOptions): Promise<MFEHttpResponse<T>>
  delete<T = unknown>(url: string, options?: MFEHttpRequestOptions): Promise<MFEHttpResponse<T>>
}

MFEHttpRequestOptions

interface MFEHttpRequestOptions {
  /** Headers aggiuntivi (Authorization gestito automaticamente) */
  headers?: Record<string, string>
  /** Query parameters */
  params?: Record<string, string | number | boolean>
  /** Timeout in millisecondi */
  timeout?: number
  /** Segnale per abort */
  signal?: AbortSignal
}

MFEHttpResponse<T>

interface MFEHttpResponse<T = unknown> {
  data: T
  status: number
  statusText: string
  headers: Headers
}

Caratteristiche garantite dalla shell

  • Authorization automatica: l'header Authorization: Bearer ... viene aggiunto dalla shell
  • Refresh token: la shell intercetta 401 e fa refresh automatico (trasparente per il MFE)
  • Base URL: tipicamente la shell prefigura il client su ctx.env.apiBaseUrl
  • Headers comuni: X-Correlation-Id, X-Trace-Id, lingua, ecc. gestiti centralmente

Il MFE non deve mai:

  • aggiungere manualmente Authorization
  • leggere/scrivere il refresh token
  • gestire 401 con redirect a login (compito della shell)

Esempi

GET con query params

const ctx = useMfeContext()

const { data, status } = await ctx.http.get<Cliente[]>('/api/clienti', {
  params: { page: 1, limit: 20, search: 'rossi' },
  timeout: 5000,
})
console.log(data.length, status) // 20, 200

POST con body

const { data } = await ctx.http.post<Cliente>('/api/clienti', {
  ragioneSociale: 'Acme SRL',
  piva: '12345678901',
})
console.log('Creato:', data.id)

Cancellazione con AbortController

const controller = new AbortController()

ctx.http
  .get<Risultato[]>('/api/ricerca', {
    params: { q: query.value },
    signal: controller.signal,
  })
  .catch((err) => {
    if (err.name === 'AbortError') return // cancellato volutamente
    throw err
  })

// Più tardi (es. nuova ricerca):
controller.abort()

Headers custom (solo casi specifici)

const { data } = await ctx.http.post<ExportJob>('/api/export', payload, {
  headers: {
    'X-Export-Format': 'xlsx',
    Accept: 'application/octet-stream',
  },
})
Non sovrascrivere Authorization: la shell lo aggiunge dopo gli headers custom. Se devi parlare con un'API che usa un token diverso, usa fetch diretto solo per quel caso, dichiarando esplicitamente che è un'eccezione.

Pattern con composables

useCrudResource accetta funzioni che usano ctx.http:

const resource = useCrudResource<Cliente, CreateClienteRequest>({
  resourceName: 'clienti',
  loadItems: async () => (await ctx.http.get<Cliente[]>('/api/clienti')).data,
  createItem: async (p) => (await ctx.http.post<Cliente>('/api/clienti', p)).data,
  updateItem: async (id, p) => (await ctx.http.put<Cliente>(`/api/clienti/${id}`, p)).data,
  deleteItem: async (id) => { await ctx.http.delete(`/api/clienti/${id}`) },
})

Implementazione lato shell

Esempio minimale di MFEHttpClient basato su fetch:

class ShellHttpClient implements MFEHttpClient {
  constructor(private readonly authProvider: () => string | null) {}

  async get<T>(url: string, options?: MFEHttpRequestOptions) {
    return this._request<T>('GET', url, undefined, options)
  }
  async post<T>(url: string, data?: unknown, options?: MFEHttpRequestOptions) {
    return this._request<T>('POST', url, data, options)
  }
  // ... put/patch/delete

  private async _request<T>(
    method: string,
    url: string,
    body?: unknown,
    opts?: MFEHttpRequestOptions,
  ): Promise<MFEHttpResponse<T>> {
    const queryString = opts?.params
      ? '?' + new URLSearchParams(opts.params as Record<string, string>).toString()
      : ''
    const token = this.authProvider()
    const res = await fetch(`${url}${queryString}`, {
      method,
      headers: {
        'Content-Type': 'application/json',
        ...(token ? { Authorization: `Bearer ${token}` } : {}),
        ...opts?.headers,
      },
      body: body ? JSON.stringify(body) : undefined,
      signal: opts?.signal,
    })
    return {
      data: (await res.json()) as T,
      status: res.status,
      statusText: res.statusText,
      headers: res.headers,
    }
  }
}

In produzione: tipicamente axios con interceptor di refresh token.

Tipi correlati