Professional HTTP client for microservice-to-microservice communication in Node.js and NestJS projects.
micro-requester is intentionally focused on one job: reliable HTTP calls between services.
- One primary API:
createClient() - Preserves upstream
4xxpayloads (like409 Conflict) instead of masking them - Normalizes transport failures into predictable gateway-style errors
- Built-in timeout and retry control with sensible defaults
- Works in plain Node.js and NestJS
If your project has custom middleware, tracing, or request context strategy, keep that in your app and pass getReqId.
npm install micro-requesterRequirements: Node.js 18+
import { createClient } from 'micro-requester';
const users = createClient({
service: 'users-service',
base: process.env.USERS_SERVICE_HTTP_URL || 'http://localhost:3001',
timeoutMs: 5000,
retries: 2,
getReqId: () => requestContext.getStore()?.requestId, // optional
});
const user = await users.get('/users/1');
const created = await users.post('/users', {
email: 'alice@example.com',
name: 'Alice',
});Creates a reusable client for one upstream service.
type ClientOptions = {
service: string;
base: string;
timeoutMs?: number;
retries?: number;
retryStatuses?: number[];
headers?: Record<string, string>;
getReqId?: () => string | undefined;
};Parameter details:
service: service label used in mapped errorsbase: upstream base URLtimeoutMs: default timeout per request (default5000)retries: default retry count (default2)retryStatuses: status codes considered retryable for idempotent methods (default[502, 503, 504])headers: default headers merged into every requestgetReqId: optional request ID resolver; falls back to generated UUID
type Client = {
req<T>(input: ReqInput): Promise<T>;
get<T>(path: string, opts?: Partial<ReqInput>): Promise<T>;
post<T>(path: string, body?: unknown, opts?: Partial<ReqInput>): Promise<T>;
put<T>(path: string, body?: unknown, opts?: Partial<ReqInput>): Promise<T>;
patch<T>(path: string, body?: unknown, opts?: Partial<ReqInput>): Promise<T>;
delete<T>(path: string, opts?: Partial<ReqInput>): Promise<T>;
};type ReqInput = {
method: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'HEAD' | 'OPTIONS';
path: string;
body?: unknown;
query?: Record<string, string | number | boolean | undefined>;
headers?: Record<string, string>;
timeoutMs?: number;
retry?: false | number;
};Notes:
- If
bodyis an object, it is JSON-stringified automatically. - If
content-typeis missing for object body,application/jsonis set. x-request-idis always attached to outgoing requests.req()supports all methods inReqInput, includingHEADandOPTIONS.
4xxresponses are passed through with original body/message5xxresponses are normalized to502 Bad Gateway
ECONNREFUSED->503 Service Unavailable- timeout/abort/
UND_ERR*->504 Gateway Timeout - other transport failures ->
502 Bad Gateway
This behavior is designed to prevent business errors (for example duplicate email 409) from being hidden by generic transport messages.
Retries are conservative by default:
- Retryable methods:
GET,HEAD,OPTIONS - Retryable statuses:
502,503,504 - Retryable transport errors:
ECONNREFUSED,ETIMEDOUT,ENOTFOUND,UND_ERR_CONNECT_TIMEOUT,UND_ERR_SOCKET
Backoff:
- Exponential:
100 * 2^attempt - Jitter: up to
50ms - Max delay:
1500ms
Override per request:
await users.get('/health', { retry: false });
await users.get('/health', { retry: 1 });import { Injectable } from '@nestjs/common';
import { createClient } from 'micro-requester';
import { requestContext } from './request-context.middleware';
@Injectable()
export class UsersClient {
private readonly client = createClient({
service: 'users-service',
base: process.env.USERS_SERVICE_HTTP_URL || 'http://localhost:3001',
timeoutMs: 5000,
retries: 2,
getReqId: () => requestContext.getStore()?.requestId,
});
getUser(id: string) {
return this.client.get(`/users/${id}`);
}
createUser(body: unknown) {
return this.client.post('/users', body);
}
}- Keep package concerns inside HTTP execution and error mapping
- Keep project concerns (middleware/context/observability) inside your app
- Preserve upstream business errors by default
MIT