@digitalbooting/request-api
v1.0.8
Published
Light Weight Request Api for Http requests support GraphQL and Rest
Maintainers
Readme
@digitalbooting/request-api
Lightweight HTTP client for JavaScript with built-in support for REST, GraphQL, encryption, middlewares, and authentication.
✨ Features
- 🚀 Lightweight - Minimal dependencies, maximum performance
- 🔄 REST & GraphQL - Full support for both paradigms
- 🔐 Built-in Encryption - AES-256 encryption for sensitive data
- 🎯 Middleware System - Chain operations before requests
- 🔑 Authentication - Bearer and Basic auth helpers
- 📦 FormData Support - File uploads made easy
- ⚡ Promise-based - Modern async/await syntax
- 🎨 Response Pattern - Consistent
[payload, error]destructuring - 🔄 Auto Header Reset - Preserves default headers across requests
📦 Installation
# npm
npm install @digitalbooting/request-api
# yarn
yarn add @digitalbooting/request-api
# pnpm
pnpm add @digitalbooting/request-api🚀 Quick Start
Basic Usage
import { createClient } from '@digitalbooting/request-api';
// Create client instance
const api = createClient('https://api.example.com', {
'Content-Type': 'application/json'
});
// Make a GET request
const [payload, error] = await api.get('/users');
if (error) {
console.error('Request failed:', error);
} else {
console.log('Users:', payload.data);
}Using Class Constructor
import ApiClient from '@digitalbooting/request-api';
const api = new ApiClient('https://api.example.com', {
'Content-Type': 'application/json',
'X-Custom-Header': 'value'
});📚 API Reference
Constructor
new ApiClient(baseURL, headers?, options?)Parameters:
baseURL(string): Base URL for all requestsheaders(object, optional): Default headers for all requestsoptions(object, optional): Additional fetch options
Example:
const api = new ApiClient('https://api.example.com', {
'Content-Type': 'application/json',
'Authorization': 'Bearer token'
}, {
mode: 'cors',
credentials: 'include'
});HTTP Methods
All methods return a Promise that resolves to [payload, error] tuple.
GET Request
await api.get(path)Example:
const [payload, error] = await api.get('/users');
if (error) {
console.error('Error:', error.data);
} else {
console.log('Users:', payload.data);
console.log('Status:', payload.status);
console.log('Success:', payload.success);
}POST Request
await api.post(path, body, isFormData?)Example with JSON:
const [payload, error] = await api.post('/users', {
name: 'John Doe',
email: '[email protected]'
});Example with FormData:
const formData = new FormData();
formData.append('file', fileInput.files[0]);
formData.append('description', 'Profile picture');
const [payload, error] = await api.post('/upload', formData, true);PUT Request
await api.put(path, body, isFormData?)Example:
const [payload, error] = await api.put('/users/123', {
name: 'Jane Doe'
});DELETE Request
await api.delete(path)Example:
const [payload, error] = await api.delete('/users/123');PURE POST (No response parsing)
For endpoints that don't return JSON:
await api.pure_post(path, body, isFormData?)Example:
const [payload, error] = await api.pure_post('/webhook', {
event: 'user.created'
});
// payload.success indicates HTTP status
// payload.status contains the HTTP status codeGraphQL
await api.graphql(query, variables?, method?)Parameters:
query(string): GraphQL query stringvariables(object, optional): Query variablesmethod(string, optional): HTTP method (default: 'POST')
Example:
const query = `
query GetUser($id: ID!) {
user(id: $id) {
id
name
email
posts {
title
content
}
}
}
`;
const [payload, error] = await api.graphql(query, { id: '123' });
if (!error) {
console.log('User:', payload.data.user);
}Mutation Example:
const mutation = `
mutation CreateUser($input: CreateUserInput!) {
createUser(input: $input) {
id
name
email
}
}
`;
const [payload, error] = await api.graphql(mutation, {
input: {
name: 'John Doe',
email: '[email protected]'
}
});Custom GraphQL Endpoint:
const api = new ApiClient('https://api.example.com', {}, {
disableDefaultGraphqlEndpoint: true
});
// Requests will go to baseURL directly
// instead of baseURL/graphqlHeaders Management
Set Headers
api.setHeaders(newHeaders)Example:
api.setHeaders({
'X-API-Key': 'your-api-key',
'X-Custom-Header': 'value'
});Reset to Default Headers
api.setDefaultHeaders()Headers are automatically reset after each request to the values passed in the constructor.
Example:
// Initial headers
const api = createClient('https://api.example.com', {
'Content-Type': 'application/json'
});
// Add temporary header
api.setHeaders({ 'X-Temp-Header': 'temp' });
// Make request
await api.get('/data');
// X-Temp-Header is automatically removed after request
// Content-Type is preserved for next requestAuthentication
Bearer Token
api.setAuth('bearer', token)Example:
const token = 'eyJhbGciOiJIUzI1NiIs...';
api.setAuth('bearer', token);
// All subsequent requests will include:
// Authorization: Bearer eyJhbGciOiJIUzI1NiIs...Basic Auth
api.setAuth('basic', 'username:password')Example:
api.setAuth('basic', 'admin:secretpass');
// All subsequent requests will include:
// Authorization: Basic YWRtaW46c2VjcmV0cGFzcw==JWT Token Check
await api.checkJWToken(token)Example:
const [isValid, error] = await api.checkJWToken(currentToken);
if (!isValid) {
console.log('Token expired:', error);
// Handle re-authentication
}Middleware System
Middlewares are executed before each request. If a middleware returns false, the request is blocked.
Register Middleware
api.registerMiddleware(asyncFunction)Example: Logging Middleware
api.registerMiddleware(async () => {
console.log('Request initiated at:', new Date().toISOString());
return true; // Continue with request
});Example: Authentication Check
api.registerMiddleware(async () => {
const token = localStorage.getItem('auth_token');
if (!token) {
console.error('No authentication token found');
return false; // Block request
}
api.setAuth('bearer', token);
return true; // Continue with request
});Example: Rate Limiting
let lastRequestTime = 0;
const MIN_INTERVAL = 1000; // 1 second
api.registerMiddleware(async () => {
const now = Date.now();
const timeSinceLastRequest = now - lastRequestTime;
if (timeSinceLastRequest < MIN_INTERVAL) {
console.warn('Rate limit: Too many requests');
return false;
}
lastRequestTime = now;
return true;
});Example: Multiple Middlewares
// Logger
api.registerMiddleware(async () => {
console.log('[API] Request starting...');
return true;
});
// Auth check
api.registerMiddleware(async () => {
const token = getToken();
if (!token) return false;
api.setAuth('bearer', token);
return true;
});
// Custom header
api.registerMiddleware(async () => {
api.setHeaders({
'X-Request-ID': generateRequestId()
});
return true;
});Encryption (AES-256)
Built-in AES-256 encryption for sensitive data transmission.
Enable Encryption
api.enableEncryption(key?, iv?)Parameters:
key(string, optional): 32-byte hex key (64 characters)iv(string, optional): 16-byte hex IV (32 characters)
Example with Auto-generated Keys:
api.enableEncryption();
// Keys are auto-generated and accessible via:
console.log('Key:', api.key);
console.log('IV:', api.iv);Example with Custom Keys:
const key = 'a'.repeat(64); // 32 bytes in hex
const iv = 'b'.repeat(32); // 16 bytes in hex
api.enableEncryption(key, iv);Set Encryption Key
api.setKey(hexKey)Example:
const key = '0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef';
api.setKey(key); // Must be 64 hex characters (32 bytes)Set Encryption IV
api.setIV(hexIV)Example:
const iv = '0123456789abcdef0123456789abcdef';
api.setIV(iv); // Must be 32 hex characters (16 bytes)Manual Encryption/Decryption
const encrypted = api.encrypt(plainText);
const decrypted = api.decrypt(encryptedText);Example:
api.enableEncryption();
const sensitive = 'Credit card: 1234-5678-9012-3456';
const encrypted = api.encrypt(sensitive);
console.log('Encrypted:', encrypted);
const decrypted = api.decrypt(encrypted);
console.log('Decrypted:', decrypted); // Original textUtility Methods
Get Client IP
await api.requestIp()Example:
const ipInfo = await api.requestIp();
console.log('Client IP:', ipInfo.ip);Getters
api.base_url // Get base URL
api.headers_enabled // Get current headers
api.options_enabled // Get fetch options
api.middlewares_enabled // Get registered middlewares
api.encryption_enabled // Check if encryption is enabled
api.key // Get encryption key
api.iv // Get encryption IVExample:
console.log('Base URL:', api.base_url);
console.log('Headers:', api.headers_enabled);
console.log('Middlewares:', api.middlewares_enabled.length);
console.log('Encrypted:', api.encryption_enabled);🎯 Response Format
All HTTP methods return a tuple [payload, error]:
Success Response
const [payload, error] = await api.get('/users');
if (!error) {
payload.data // Response data
payload.success // true
payload.status // HTTP status code (200, 201, etc.)
}Error Response
const [payload, error] = await api.post('/users', invalidData);
if (error) {
error.data // Error data from server
error.response // Raw response object
error.success // false
error.status // HTTP status code (400, 500, etc.)
}Usage Pattern
// Destructure and check
const [payload, error] = await api.get('/users');
if (error) {
console.error('Request failed:', error.data);
return;
}
console.log('Success:', payload.data);💡 Advanced Examples
Complete Authentication Flow
import { createClient } from '@digitalbooting/request-api';
const api = createClient('https://api.example.com', {
'Content-Type': 'application/json'
});
// Login
async function login(email, password) {
const [payload, error] = await api.post('/auth/signin', {
email,
password
});
if (error) {
throw new Error(error.data?.message || 'Login failed');
}
// Store token
localStorage.setItem('auth_token', payload.data.token);
// Set for future requests
api.setAuth('bearer', payload.data.token);
return payload.data.user;
}
// Logout
async function logout() {
const [payload, error] = await api.post('/auth/signout');
localStorage.removeItem('auth_token');
api.setDefaultHeaders(); // Clear auth header
return !error;
}
// Auto-attach token middleware
api.registerMiddleware(async () => {
const token = localStorage.getItem('auth_token');
if (token) {
api.setAuth('bearer', token);
}
return true;
});File Upload with Progress
async function uploadFile(file, onProgress) {
const formData = new FormData();
formData.append('file', file);
formData.append('timestamp', Date.now());
// Add progress tracking middleware
api.registerMiddleware(async () => {
if (onProgress) {
onProgress({ status: 'uploading', progress: 0 });
}
return true;
});
const [payload, error] = await api.post('/upload', formData, true);
if (error) {
if (onProgress) {
onProgress({ status: 'error', error: error.data });
}
return null;
}
if (onProgress) {
onProgress({ status: 'complete', data: payload.data });
}
return payload.data;
}
// Usage
uploadFile(myFile, (progress) => {
console.log('Upload:', progress);
});API Service Class
import { createClient } from '@digitalbooting/request-api';
class UserService {
constructor(baseURL) {
this.api = createClient(baseURL, {
'Content-Type': 'application/json'
});
this.setupMiddlewares();
}
setupMiddlewares() {
// Auth middleware
this.api.registerMiddleware(async () => {
const token = this.getToken();
if (token) {
this.api.setAuth('bearer', token);
}
return true;
});
// Logging middleware
this.api.registerMiddleware(async () => {
console.log('[UserService] Request initiated');
return true;
});
}
getToken() {
return localStorage.getItem('auth_token');
}
async getAll() {
const [payload, error] = await this.api.get('/users');
if (error) throw new Error(error.data?.message);
return payload.data;
}
async getById(id) {
const [payload, error] = await this.api.get(`/users/${id}`);
if (error) throw new Error(error.data?.message);
return payload.data;
}
async create(userData) {
const [payload, error] = await this.api.post('/users', userData);
if (error) throw new Error(error.data?.message);
return payload.data;
}
async update(id, userData) {
const [payload, error] = await this.api.put(`/users/${id}`, userData);
if (error) throw new Error(error.data?.message);
return payload.data;
}
async delete(id) {
const [payload, error] = await this.api.delete(`/users/${id}`);
if (error) throw new Error(error.data?.message);
return payload.success;
}
}
// Usage
const userService = new UserService('https://api.example.com');
const users = await userService.getAll();
const user = await userService.create({ name: 'John', email: '[email protected]' });GraphQL with Encryption
const api = createClient('https://api.example.com');
// Enable encryption
api.enableEncryption();
// Send encrypted GraphQL query
const query = `
query GetSensitiveData {
user {
ssn
creditCard
}
}
`;
const [payload, error] = await api.graphql(query);
// The query is automatically encrypted before sending
// Response is automatically decrypted (if server supports it)Error Handling Patterns
// Pattern 1: Inline checking
const [payload, error] = await api.get('/users');
if (error) {
console.error('Error:', error.data);
return;
}
console.log('Data:', payload.data);
// Pattern 2: Try-catch wrapper
async function fetchUsers() {
const [payload, error] = await api.get('/users');
if (error) {
throw new Error(error.data?.message || 'Failed to fetch users');
}
return payload.data;
}
try {
const users = await fetchUsers();
console.log('Users:', users);
} catch (err) {
console.error('Error:', err.message);
}
// Pattern 3: Custom error handler
function handleApiError(error) {
if (error.status === 401) {
// Unauthorized - redirect to login
window.location.href = '/login';
} else if (error.status === 403) {
// Forbidden
alert('You do not have permission to perform this action');
} else if (error.status === 500) {
// Server error
console.error('Server error:', error.data);
} else {
// Generic error
console.error('Request failed:', error.data);
}
}
const [payload, error] = await api.get('/users');
if (error) {
handleApiError(error);
return;
}
console.log('Data:', payload.data);🔧 Configuration
Fetch Options
You can pass any valid fetch options:
const api = createClient('https://api.example.com', {
'Content-Type': 'application/json'
}, {
mode: 'cors',
credentials: 'include',
cache: 'no-cache',
redirect: 'follow',
referrerPolicy: 'no-referrer'
});Custom Options per Request
api.setOptions({
signal: abortController.signal,
priority: 'high'
});
const [payload, error] = await api.get('/users');📝 Best Practices
1. Use Destructuring Pattern
// ✅ Good
const [payload, error] = await api.get('/users');
if (error) {
// Handle error
}
// ❌ Bad
const response = await api.get('/users');
if (response[1]) {
// Handle error
}2. Check Errors First
// ✅ Good
const [payload, error] = await api.post('/users', data);
if (error) {
console.error('Failed:', error.data);
return;
}
console.log('Success:', payload.data);
// ❌ Bad
const [payload, error] = await api.post('/users', data);
console.log('Success:', payload.data); // May crash if error3. Reuse Client Instances
// ✅ Good - Single instance
// api.js
export const api = createClient('https://api.example.com', {
'Content-Type': 'application/json'
});
// users.js
import { api } from './api';
const [payload, error] = await api.get('/users');
// ❌ Bad - Multiple instances
const api1 = createClient('https://api.example.com');
const api2 = createClient('https://api.example.com');4. Use Middlewares for Cross-cutting Concerns
// ✅ Good - Centralized auth
api.registerMiddleware(async () => {
const token = getToken();
if (token) api.setAuth('bearer', token);
return true;
});
// ❌ Bad - Manual auth each time
api.setAuth('bearer', getToken());
await api.get('/users');
api.setAuth('bearer', getToken());
await api.get('/posts');5. Handle FormData Correctly
// ✅ Good
const formData = new FormData();
formData.append('file', file);
const [payload, error] = await api.post('/upload', formData, true);
// ^^^^ isFormData
// ❌ Bad - Will stringify FormData
const [payload, error] = await api.post('/upload', formData);🐛 Troubleshooting
Headers Not Persisting
Problem: Headers are lost after first request.
Solution: This was fixed in v1.0.7. Make sure you're using the latest version:
npm install @digitalbooting/request-api@latestCORS Errors
Problem: Request blocked by CORS policy.
Solution: Configure fetch options:
const api = createClient('https://api.example.com', {
'Content-Type': 'application/json'
}, {
mode: 'cors',
credentials: 'include'
});FormData Not Sending
Problem: FormData sent as JSON.
Solution: Pass true as third parameter:
const [payload, error] = await api.post('/upload', formData, true);
// ^^^^Middleware Blocking Requests
Problem: Request never executes.
Solution: Ensure middleware returns true:
api.registerMiddleware(async () => {
// Your logic here
return true; // ← Must return true to continue
});📊 Changelog
v1.0.7 (Current)
- 🐛 Fix: Default headers now properly preserved across requests
- 🔧 Fix:
#initialHeaderscorrectly initialized in constructor - ✨ Improvement: Headers automatically reset to constructor values after each request
v1.0.6
- 🐛 Bug: Headers reset to empty object after each request (fixed in 1.0.7)
📄 License
ISC © Leonardo Quintana
🤝 Contributing
Contributions are welcome! Please follow these guidelines:
- Fork the repository
- Create a feature branch
- Commit your changes
- Push to the branch
- Open a Pull Request
📮 Support
- Issues: GitLab Issues
- Repository: GitLab
- NPM: @digitalbooting/request-api
🌟 Show Your Support
Give a ⭐️ if this project helped you!
Made with ❤️ by Digital Booting
