Custom API & Transforms
Custom API & Transforms Use the client's HTTP methods when your third-party application calls a custom Enfyra route, needs a non-table response, downloads another response type, or wants request-specific transformation. Typed HTTP Methods interface PublishResult { articleId: numb
Custom API & Transforms
Use the client's HTTP methods when your third-party application calls a custom Enfyra route, needs a non-table response, downloads another response type, or wants request-specific transformation.
Typed HTTP Methods
interface PublishResult {
articleId: number
publishedAt: string
}
const { data } = await enfyra.post<PublishResult>('/articles/publish', {
articleId: 42,
})
Available methods:
enfyra.get<T>(endpoint, options?)
enfyra.post<T>(endpoint, body?, options?)
enfyra.put<T>(endpoint, body?, options?)
enfyra.patch<T>(endpoint, body?, options?)
enfyra.delete<T>(endpoint, options?)
Query Parameters and Headers
const response = await enfyra.get<Report>('/reports/monthly', {
query: {
month: '2026-07',
locale: 'en',
},
headers: {
'x-request-purpose': 'dashboard',
},
})
Public routes use the same client and need no special authentication configuration:
const { data } = await enfyra.get<PublicStatus>('/public/status')
Response Types
JSON is the default. Select another type when needed:
const { data: text } = await enfyra.get<string>('/exports/preview', {
responseType: 'text',
})
const { data: blob } = await enfyra.get<Blob>('/exports/latest', {
responseType: 'blob',
})
Supported values are json, text, blob, and arrayBuffer.
Per-Request Transform
Use transform when one call needs a custom result shape. The transformer receives and must return the full HttpResponse:
interface ApiArticle {
id: number
title: string
}
interface ArticleOption {
value: number
label: string
}
const response = await enfyra.get<ArticleOption[]>('/articles', {
transform: (current) => ({
...current,
data: (current.data as unknown as ApiArticle[]).map((article) => ({
value: article.id,
label: article.title,
})),
}),
})
Preserve status, headers, and meta unless you intentionally change them.
For table query data only, .from(table).transform() is shorter because it receives data directly.
Global Response Transform
Register a transformer in client configuration:
const enfyra = new EnfyraClient({
baseUrl: 'https://admin.example.com',
onResponse: (response) => ({
...response,
data: normalizeDates(response.data),
}),
})
You may provide one function or an array. You can also register another transformer later:
enfyra.onResponse(async (response) => {
await metrics.record(response.status)
return response
})
Global transformers run in registration order. A per-request transformer runs after the global transformers.
Global Error Transform
import { EnfyraClient, EnfyraError } from '@enfyra/sdk-core'
const enfyra = new EnfyraClient({
baseUrl: 'https://admin.example.com',
onError: (error) => {
if (error.code === 'RATE_LIMITED') {
return new EnfyraError({
code: error.code,
statusCode: error.statusCode,
details: error.details,
message: 'Please wait before trying again.',
})
}
return error
},
})
An error transformer must return an EnfyraError. Preserve the original code, status, and structured details when changing the displayed message.
Handle Errors at the Call Site
import { isEnfyraError } from '@enfyra/sdk-core'
try {
await enfyra.post('/articles/publish', { articleId })
} catch (error) {
if (!isEnfyraError(error)) throw error
switch (error.code) {
case 'UNAUTHORIZED':
return openLogin()
case 'FORBIDDEN':
return showMessage('You do not have access to publish this article.')
case 'VALIDATION_ERROR':
return showValidation(error.details)
case 'RATE_LIMITED':
return showMessage('Please wait and try again.')
default:
return showMessage(error.message)
}
}
Known codes include UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_ERROR, RATE_LIMITED, SERVER_ERROR, NETWORK_ERROR, and TIMEOUT_ERROR. Enfyra may also return a more specific string code.
Free-Form Fetch
Use enfyra.fetch() when a library expects the standard Fetch API:
const response = await enfyra.fetch('/custom-report', {
method: 'POST',
headers: { 'content-type': 'application/json' },
body: JSON.stringify({ range: 'month' }),
})
Relative URLs resolve from the client base. Absolute URLs remain absolute. Enfyra credentials are never attached to an absolute URL outside the configured Enfyra base scope.
Use an Existing Axios Instance
import axios from 'axios'
const api = axios.create({ baseURL: 'https://admin.example.com' })
enfyra.attachAxios(api)
const response = await api.get('/api/me')
The attachment is idempotent, attaches credentials only to Enfyra-scoped requests, and retries one eligible 401 after the SDK refreshes the session. Release interceptors when the integration is torn down:
enfyra.dispose()
Cancellation and Unsafe Retries
const controller = new AbortController()
const request = enfyra.post('/reports/build', input, {
signal: controller.signal,
timeout: 60_000,
retryUnsafe: false,
})
controller.abort()
Enable retryUnsafe: true only when the endpoint is idempotent or provides its own idempotency key.