@dccs/teff-rc
v1.1.0
Published
A robust and extensible REST client built on the Web Fetch API, with request/response filters, content-type handlers and centralized error handling.
Readme
@dccs/teff-rc
A robust and extensible REST client built on the Web Fetch API, providing powerful features for enhanced request handling, response processing, and error management. teff-rc streamlines your API interactions, offering a flexible and type-safe solution for modern web applications.
✨ Features
- Type-Safe API Interactions: Leverage TypeScript for strong typing throughout your REST requests and responses.
- Extensible Request/Response Filters: Implement custom logic before requests are sent and after responses are received.
- Custom Content Type Handling: Easily register handlers for various
Content-Typeheaders, including JSON, plain text, and binary data (PDF, Octet-Stream, Zip). - Centralized Error Handling: Robust error management with
FetchResponseErrorfor detailed insights into failed requests, including HTTP status, response body, and custom error references. - Request Context & Lifecycle Hooks: Gain deep insights and control over the request lifecycle with unique request IDs, start times, and optional context hooks.
- Automatic JSON Serialization: Intelligently handles request body serialization for common data types (objects, strings, numbers, booleans) to JSON.
- Root Path Configuration: Easily configure a base URL for all your REST requests.
- Modern Web Standards: Built directly on the native Fetch API, ensuring compatibility and leveraging browser optimizations.
- Diagnostics You Can Switch On In Production: Every request narrates itself through
@dccs/teff-log— atDEBUG, so it costs you nothing until you raise the log level.
📦 Installation
npm install @dccs/teff-rc🚀 Usage
import { FetchRestClient } from '@dccs/teff-rc';
const client = new FetchRestClient();
client.setRootPath('/api');
const user = await client.request<User>({
method: 'GET',
url: '/users/42'
});🔍 Debugging a request
The instrumentation is not stripped from production builds. The traffic records ship at DEBUG and
stay quiet at the default level, so a request misbehaving in a deployed app can be inspected where it
misbehaves — no rebuild, no local reproduction:
globalThis.__teff__.logger.level = 'DEBUG'; // browser console, takes effect immediatelyhttps://app.example.com/#/orders?teffLogLevel=DEBUG // or from the URL, for hash routersimport { Logger, LogLevel } from '@dccs/teff-log';
Logger.setLevel(LogLevel.DEBUG); // or from application codeEvery record leads with the request it belongs to, so a busy console is scanned by request id:
[DEBUG] teff-rc #7 POST /rest/orders request {headers: ['Authorization', 'Content-Type'], body: 'string(184)'}
[DEBUG] teff-rc #7 POST /rest/orders response {status: 201, ok: true, ms: 96, contentType: 'application/json'}What each level means
Only DEBUG has to be switched on. WARN and ERROR pass @dccs/teff-log's default level (INFO),
so they show up without anyone opting in — which is the whole point of them.
| Level | Default | Used for |
| --- | --- | --- |
| DEBUG | quiet | The normal life of a request: sent, answered, filtered — including 4xx and 5xx. An error status is an answer, and callers routinely expect one (a 401 before a token refresh, a 400 from a validation endpoint). |
| WARN | visible | The client cannot do what it was asked: a content type with no registered handler, a response with no Content-Type, a body that will not serialize, markErrorHandled() called where it has no effect. These are defects, so they do not wait to be asked for. |
| ERROR | visible | The request produced no response at all — offline, DNS, CORS, TLS, abort. |
The WARN records exist because the accompanying exception is sometimes thinner than the problem.
No response handler found for: text/html does not say which request it came from, or what the
alternatives were. The record does:
[WARN] teff-rc #7 GET /rest/report no response handler
{contentType: 'text/html', registered: ['application/json', 'text/plain', 'application/pdf', …]}Filters get the same treatment, since a filter that calls stop() or markErrorHandled() is otherwise
invisible — and is the usual reason a filter someone registered appears to do nothing, or an error
appears to vanish.
What is never logged
Header values, request or response bodies, and query parameter values. Authorization and
Cookie carry credentials; bodies and query strings carry personal data (?email=, ?ssn=, whatever
someone typed into a search box); and all of it would otherwise be retained in the buffer that
Logger.getMessages() hands to whoever is writing a bug report. So a query string is recorded by name
only:
GET /rest/[email protected]&status=OPEN&status=SENT
[DEBUG] teff-rc #9 GET /rest/orders?customerEmail&status request {headers: […], body: 'none'}Header names, query parameter names, body kind and body size answer nearly every question the values would; the browser's network panel is still there for the rest.
The one thing this cannot redact is a path segment — /users/[email protected] is indistinguishable
from /users/42 from inside the client. A route that puts personal data in the path puts it in these
records too.
📄 License
MIT
