Fetch API with middleware - simple, powerful, and TypeScript-first.
fej provides a clean middleware API for the native Fetch API, allowing you to modify request properties, handle errors, implement retries, and more.
use() method for all middlewarenpm install fej
import { createFej } from 'fej';
// Create a new instance
const api = createFej();
// Make a request
const response = await api.fej('https://api.example.com/users');
const data = await response.json();
import { createFej } from 'fej';
const api = createFej({
baseURL: 'https://api.example.com',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
},
timeout: 5000,
retry: {
attempts: 3,
delay: 1000,
backoff: 'exponential',
},
});
// Make requests with base configuration
const response = await api.fej('/users'); // https://api.example.com/users
Middleware uses a Koa-style onion model with async/await:
import { createFej } from 'fej';
const api = createFej();
// Add authentication middleware
api.use('auth', async (ctx, next) => {
// Before request
const token = await getAuthToken();
ctx.request.init.headers = new Headers(ctx.request.init.headers);
ctx.request.init.headers.set('Authorization', `Bearer ${token}`);
await next(); // Execute request
// After response (optional)
console.log(`Status: ${ctx.response?.status}`);
});
// Make request with middleware
const response = await api.fej('https://api.example.com/protected');
Control execution order with priority (higher runs first):
api.use('auth', authMiddleware, 100); // Runs first
api.use('logger', loggerMiddleware, 50); // Runs second
api.use('retry', retryMiddleware, 10); // Runs third
api.use('logger', async (ctx, next) => {
const start = Date.now();
console.log(`โ ${ctx.request.init.method || 'GET'} ${ctx.request.url}`);
await next();
const duration = Date.now() - start;
console.log(`โ ${ctx.response?.status} (${duration}ms)`);
});
const { controller, requestId } = api.createAbortController();
// Make cancellable request
const fetchPromise = api.fej('https://api.example.com/data', {
signal: controller.signal,
});
// Cancel the request
api.abortRequest(requestId);
Cancel groups of related requests:
// Tag requests
const { controller: c1 } = api.createAbortController(undefined, ['dashboard']);
const { controller: c2 } = api.createAbortController(undefined, ['dashboard']);
const p1 = api.fej('/users', { signal: c1.signal });
const p2 = api.fej('/stats', { signal: c2.signal });
// Cancel all dashboard requests
api.abortRequestsByTag('dashboard');
api.addErrorTransform(async (error, ctx) => {
// Add context to errors
const enhancedError = new Error(
`Request to ${ctx.request.url} failed: ${error.message}`
);
enhancedError.stack = error.stack;
return enhancedError;
});
api.setDefaultRetry({
attempts: 5,
delay: 2000,
maxDelay: 30000,
backoff: 'exponential',
});
Create separate instances for different APIs:
const userApi = createFej({
baseURL: 'https://api.example.com',
retry: { attempts: 3 },
});
const paymentApi = createFej({
baseURL: 'https://payments.example.com',
retry: { attempts: 10 }, // More retries for critical operations
timeout: 30000,
});
// Each instance has independent configuration
const users = await userApi.fej('/users');
const payment = await paymentApi.fej('/charge');
We welcome contributions! Please see our Contributing Guide for details.
# Clone the repository
git clone https://github.com/maxali/fej.git
cd fej
# Install dependencies
npm install
# Build the project
npm run build
# Run tests
npm test
# Run linter
npm run lint
ISC License - see LICENSE file for details
If you find fej useful, please consider: