niafix-utils
v1.2.0
Published
Enterprise-grade TypeScript HTTP client library with intelligent caching, OData query builder, FormData support, and comprehensive utilities for modern web development. Framework-agnostic design works seamlessly across all JavaScript/TypeScript environmen
Maintainers
Readme
Niafix Utils - Enterprise HTTP Client & Utilities
A production-ready, enterprise-grade TypeScript HTTP client library designed for modern web applications. Built with a modular, framework-agnostic architecture, niafix-utils provides comprehensive HTTP request management, intelligent caching, OData query building, and advanced FormData handling across all JavaScript and TypeScript environments.
Overview
niafix-utils is engineered to deliver robust, scalable API communication capabilities for enterprise applications. The library offers a clean, intuitive API while maintaining flexibility for complex use cases. Whether you're building client-side applications, server-side services, or full-stack solutions, niafix-utils seamlessly integrates into your technology stack.
Key Features
- Enterprise-Grade HTTP Client: Type-safe, feature-rich HTTP client with comprehensive request/response handling
- Modular Architecture: Clean separation of concerns with RequestManager, ResponseManager, and ErrorHandler
- Intelligent Caching: Automatic GET request caching with configurable TTL and manual invalidation
- Advanced Logging: Detailed request/response logging with environment-aware log levels for easy debugging
- OData Query Builder: Fluent API for constructing complex OData queries with full type safety
- Advanced FormData Support: Automatic conversion of nested objects, arrays, dates, and file uploads
- Multi-API Management: Effortlessly manage multiple API endpoints with separate client instances
- Server-Side Rendering (SSR) Support: Full compatibility with SSR frameworks including Next.js and SvelteKit
- Comprehensive Error Handling: Detailed error information with status codes, messages, and request context
Installation
npm install niafix-utilsyarn add niafix-utilspnpm add niafix-utilsQuick Start
Basic Usage
import { HttpClient } from 'niafix-utils';
// Initialize the HTTP client
const client = new HttpClient({
baseURL: 'https://api.example.com',
logLevel: 'debug', // Enable detailed logging for debugging
});
// Make a GET request
const response = await client.get('/users');
console.log(response.data); // Response data
console.log(response.status); // HTTP status code
console.log(response.success); // booleanMultiple API Sources
Create separate client instances for different API endpoints:
import { HttpClient } from 'niafix-utils';
const mainAPI = new HttpClient({
baseURL: 'https://api.main.com',
logLevel: 'auto',
});
const authAPI = new HttpClient({
baseURL: 'https://auth.api.com',
logLevel: 'auto',
});
// Each client automatically uses its configured baseURL
const users = await mainAPI.get('/users');
const login = await authAPI.post('/login', credentials);Configuration
HttpClient Configuration
const client = new HttpClient({
// Required: Base URL for all requests
baseURL: 'https://api.example.com',
// Optional: Log level ('auto', 'none', 'error', 'warn', 'info', 'debug')
// 'auto' automatically uses 'debug' in development and 'error' in production
logLevel: 'auto',
// Optional: Cache TTL in milliseconds (default: 300000 = 5 minutes)
cacheTTL: 300000,
// Optional: Default headers for all requests
defaultHeaders: {
'X-Custom-Header': 'value',
'Accept-Language': 'en-US',
},
// Optional: Request credentials (default: 'same-origin')
// Use 'include' only if backend allows credentials with specific origin (not wildcard *)
credentials: 'same-origin',
});HTTP Methods
GET Requests
// Simple GET request
const response = await client.get('/users');
// GET with query parameters
const users = await client.get('/users', {
query: { page: 1, limit: 10 },
headers: { 'Accept-Language': 'en-US' },
});
// Skip cache and fetch fresh data
const freshData = await client.get('/users', { skipCache: true });
// Disable cache for specific request
const noCacheData = await client.get('/users', { noCache: true });POST Requests
// POST with JSON data
const newUser = await client.post('/users', {
name: 'John Doe',
email: '[email protected]',
age: 30,
});
// POST with FormData (automatic conversion)
const userWithFile = await client.post(
'/users',
{
name: 'Jane Doe',
email: '[email protected]',
avatar: fileObject, // File object
tags: ['tag1', 'tag2'],
},
{
useFormData: true, // Automatically converts to FormData
}
);PUT & PATCH Requests
// PUT - Full update
const updated = await client.put('/users/123', {
name: 'John Updated',
email: '[email protected]',
});
// PATCH - Partial update
const patched = await client.patch('/users/123', {
status: 'inactive',
});DELETE Requests
// Simple DELETE
await client.delete('/users/123');
// DELETE with query parameters
await client.delete('/users', {
query: { status: 'inactive' },
});Logging & Debugging
Log Levels
The library provides comprehensive logging capabilities to help you debug requests and responses:
const client = new HttpClient({
baseURL: 'https://api.example.com',
logLevel: 'debug', // Enable detailed logging
});Log Levels:
'debug': Full request/response details including headers, body, and timing'info': Request information without response details'warn': Only warnings'error': Only errors'none': No logging'auto': Automatically uses 'debug' in development, 'error' in production
Debug Mode
When logLevel: 'debug' is enabled, you'll see detailed logs in the console:
[DEBUG] [RequestBuilder] Request prepared {
method: 'POST',
url: 'https://api.example.com/users',
headers: { 'Content-Type': 'application/json', ... },
body: { name: 'John', email: '[email protected]' }
}
[DEBUG] [ResponseManager] Success response {
status: 201,
statusText: 'Created',
contentType: 'application/json',
headers: { ... },
data: { id: 1, name: 'John', ... },
dataSize: '245 chars'
}
[DEBUG] [RequestManager] Request completed in 234msError Logging
Errors are automatically logged with full context:
try {
await client.post('/users', userData);
} catch (error) {
// Error is automatically logged with:
// - Status code
// - Error message
// - Request URL and method
// - Response headers and data
// - Timestamp
console.error(error);
}OData Query Builder
Build complex OData queries with a fluent API:
import { HttpClient, ODataQueryBuilder } from 'niafix-utils';
// Using query builder
const queryBuilder = new ODataQueryBuilder()
.filter({
name: { contains: 'John' },
age: { gt: 18, lt: 65 },
status: { eq: 'active' },
})
.select('id', 'name', 'email')
.top(20)
.skip(0)
.orderBy('createdAt', 'desc');
const response = await client.getWithOData('/users', queryBuilder);
// Or using helper functions
import { filterBy, and, or } from 'niafix-utils';
const complexQuery = new ODataQueryBuilder()
.filter(and(filterBy('age', 'gt', 18), or(filterBy('city', 'eq', 'Istanbul'), filterBy('city', 'eq', 'Ankara'))))
.top(20);
const users = await client.getWithOData('/users', complexQuery);FormData Usage
Automatic FormData Conversion
// Simple FormData conversion
await client.post(
'/upload',
{
file: fileObject,
name: 'document.pdf',
tags: ['important', 'archive'],
},
{
useFormData: true, // Automatically converts to FormData
}
);Advanced FormData with Nested Objects
import { ObjectToFormData } from 'niafix-utils';
const formData = ObjectToFormData(
{
user: {
name: 'John Doe',
email: '[email protected]',
profile: {
avatar: fileObject,
bio: 'Software developer',
},
},
tags: ['tag1', 'tag2'],
files: [file1, file2],
},
{
arrayFormat: 'brackets',
includeNull: false,
}
);
await client.post('/upload', formData);Cache Management
Manual Caching
Caching is disabled by default for all requests. To enable caching for a specific GET request, provide a cacheTTL (in milliseconds):
// This request will NOT be cached
const users = await client.get('/users');
// This request WILL be cached for 60 seconds
const cachedUsers = await client.get('/users', { cacheTTL: 60000 });
// Subsequent requests with the same URL and params within 60s will return from cacheAdvanced Cache Control: skipCache vs noCache
Understanding the difference between these two options is critical for proper state management in production:
| Option | Description | Use Case |
| :-------------- | :------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------- |
| noCache | Total Bypass. The client will neither check the cache nor store the new response in the cache. | Use for sensitive data or highly volatile resources that should never be cached. |
| skipCache | Force Refresh. The client ignores existing cache, fetches fresh data, and updates the cache with the new response. | Use when you want to ensure the user sees the latest data but want subsequent calls to benefit from caching. |
[!TIP] > Hardcoding Warning: If you hardcode
skipCache: truein your source code, it will naturally fetch fresh data every time (likenoCache). Its true power lies in being passed dynamically (e.g., when a user clicks a "Refresh" button) to update the existing cache without waiting for TTL to expire.
Global Cache Control with Interceptors
You can use onBeforeRequest to control caching globally for all requests (e.g., during testing or based on a toggle):
const client = new HttpClient({
baseURL: 'https://api.example.com',
interceptors: {
onBeforeRequest: async (config) => {
// Forcefully disable caching globally
if (process.env.DISABLE_API_CACHE === 'true') {
config.noCache = true;
}
return config;
},
},
});Automatic SSR/CSR Cookie Handling
In Next.js or other SSR environments, you can automatically handle cookies so that server-side requests act as the user, while client-side requests let the browser handle it.
const client = new HttpClient({
baseURL: 'https://api.example.com',
interceptors: {
onBeforeRequest: async (config) => {
// Check if we are on the server (SSR)
if (typeof window === 'undefined') {
const { cookies } = await import('next/headers');
const cookieStore = await cookies();
// Inject credentials into the config for the server
config.cookies = cookieStore.toString();
}
// On the client (CSR), we do nothing.
// The browser's native credentials management will take over.
return config;
},
},
});Manual Cache Control
// Clear all cache
client.clearCache();
// Invalidate cache for specific URL
client.invalidateCache('/users');Logging & Interceptors
Request Data Logging
By default, the internal logger outputs the outgoing data payload in debug mode.
Custom Logging with Interceptors
You can also use onBeforeRequest to implement your own custom logging or debugging logic:
const client = new HttpClient({
baseURL: 'https://api.example.com',
interceptors: {
onBeforeRequest: async (config) => {
// Your custom logging here
console.log(`[Custom Log] Sending ${config.method} to ${config.url || ''}`);
if (config.data) {
console.log('[Custom Log] Payload:', config.data);
}
return config;
},
},
});Cache Invalidation
Mutating operations automatically invalidate related cache entries:
// POST, PUT, PATCH, DELETE automatically invalidate related cache
await client.post('/users', newUser); // Invalidates /users cache
await client.put('/users/123', updatedUser); // Invalidates /users cache
await client.delete('/users/123'); // Invalidates /users cacheAuthentication & Authorization
Client-Side Requests
Cookies are automatically sent via credentials: 'include':
const client = new HttpClient({
baseURL: 'https://api.example.com',
});
// Cookies are automatically sent
const response = await client.get('/users');CORS Configuration
Important: When using credentials: 'include', your backend must not use Access-Control-Allow-Origin: *. You must specify the exact origin:
Backend Configuration (.NET Core example):
app.UseCors(options =>
{
options.WithOrigins("http://localhost:3001") // Specific origin, not *
.AllowCredentials() // Required for credentials: 'include'
.AllowAnyMethod()
.AllowAnyHeader();
});If you don't need cookies, you can use credentials: 'omit':
const client = new HttpClient({
baseURL: 'https://api.example.com',
credentials: 'omit', // No cookies sent, CORS works with *
});Server-Side Requests
Provide cookie string explicitly:
// Next.js example
import { cookies } from 'next/headers';
const cookieStore = await cookies();
const cookieString = cookieStore
.getAll()
.map((c) => `${c.name}=${c.value}`)
.join('; ');
const response = await client.get('/users', {
cookies: cookieString,
});Bearer Token Authentication
const response = await client.get('/users', {
token: 'your-bearer-token-here',
});Interceptors (Request & Response Hooks)
You can globally intercept and modify requests before they are sent, or modify responses before they return to your application. This is ideal for injecting authentication tokens, attaching global cookies, or custom logging.
const client = new HttpClient({
baseURL: 'https://api.example.com',
interceptors: {
// Runs before every request is sent
onBeforeRequest: async (config) => {
// Modify the config (e.g., inject cookies or headers)
return {
...config,
cookies: 'session=12345',
headers: {
...config.headers,
'X-App-Version': '1.0.0',
},
};
},
// Runs after every successful response
onAfterResponse: async (response) => {
// Log the response or transform the data
console.log(`API returned status: ${response.status}`);
return response; // Return the (optionally modified) response
},
},
});Error Handling
Comprehensive error information is provided:
try {
const response = await client.get('/users');
} catch (error: any) {
console.error('Status Code:', error.statusCode);
console.error('Message:', error.message);
console.error('Details:', error.details);
console.error('Config:', error.config);
console.error('Base URL:', error.baseUrl);
// Handle specific status codes
switch (error.statusCode) {
case 401:
// Redirect to login
window.location.href = '/login';
break;
case 403:
// Access denied
alert('Access denied');
break;
case 404:
// Not found
alert('Resource not found');
break;
case 500:
// Server error
alert('Server error occurred');
break;
}
}Server-Side Rendering (SSR) Support
Next.js App Router - Server Components
// app/users/page.tsx
import { cookies } from 'next/headers';
import { HttpClient } from 'niafix-utils';
export default async function UsersPage() {
const cookieStore = await cookies();
const cookieString = cookieStore
.getAll()
.map((c) => `${c.name}=${c.value}`)
.join('; ');
const client = new HttpClient({
baseURL: process.env.API_URL!,
});
const { data } = await client.get('/users', {
cookies: cookieString,
});
return (
<div>
{data?.map((user) => (
<div key={user.id}>{user.name}</div>
))}
</div>
);
}Next.js App Router - Client Components
// app/components/UsersList.tsx
'use client';
import { HttpClient } from 'niafix-utils';
import { useEffect, useState } from 'react';
export default function UsersList() {
const [users, setUsers] = useState([]);
const client = new HttpClient({
baseURL: process.env.NEXT_PUBLIC_API_URL!,
});
useEffect(() => {
client.get('/users').then((response) => {
setUsers(response.data || []);
});
}, []);
return (
<div>
{users.map((user) => (
<div key={user.id}>{user.name}</div>
))}
</div>
);
}Best Practices
1. Multiple API Management
// lib/api/clients.ts
import { HttpClient } from 'niafix-utils';
export const mainAPI = new HttpClient({
baseURL: process.env.NEXT_PUBLIC_MAIN_API_URL!,
logLevel: 'auto',
});
export const authAPI = new HttpClient({
baseURL: process.env.NEXT_PUBLIC_AUTH_API_URL!,
logLevel: 'auto',
});2. Type Safety
interface User {
id: string;
name: string;
email: string;
}
interface UserResponse {
data: User[];
total: number;
}
const response = await client.get<UserResponse>('/users');
const users: User[] = response.data?.data || [];3. Error Handling Wrapper
export async function handleApiRequest<T>(request: () => Promise<CustomFetchResponse<T>>): Promise<T> {
try {
const response = await request();
if (response.success) {
return response.data as T;
}
throw new Error('Request failed');
} catch (error: any) {
if (error.statusCode === 401) {
window.location.href = '/login';
}
throw error;
}
}
// Usage
const users = await handleApiRequest(() => client.get('/users'));API Reference
HttpClient Methods
| Method | Description | Usage |
| -------------------------------------- | -------------------- | --------------------- |
| get<T>(url, config?) | GET request | Retrieve data |
| post<T>(url, data?, config?) | POST request | Create resource |
| put<T>(url, data?, config?) | PUT request | Update resource |
| patch<T>(url, data?, config?) | PATCH request | Partial update |
| delete<T>(url, config?) | DELETE request | Remove resource |
| head<T>(url, config?) | HEAD request | Get headers |
| options<T>(url, config?) | OPTIONS request | Get allowed methods |
| request<T>(url, config?) | Generic request | Custom methods |
| odata() | Create OData builder | Build OData queries |
| getWithOData<T>(url, query, config?) | GET with OData | Execute OData query |
| clearCache() | Clear all cache | Remove cached data |
| getCacheStats() | Get cache stats | Monitor cache |
| invalidateCache(url) | Invalidate cache | Remove specific cache |
RequestConfig Options
| Option | Type | Description |
| ------------- | ------------- | ------------------- |
| method | string | HTTP method |
| headers | object | Custom headers |
| data | any | Request body |
| query | object/string | Query parameters |
| cookies | string | Cookie string (SSR) |
| token | string | Bearer token |
| skipCache | boolean | Force fresh request |
| noCache | boolean | Disable caching |
| useFormData | boolean | Convert to FormData |
| odataQuery | string | OData query string |
| baseUrl | string | Override base URL |
