Connection Pooling
Shared http.Agent per base URL with TCP keep-alive. Zero handshake overhead. Prevents ECONNRESET from idle sockets. Auto-configured — no setup required.
Production-grade HTTP + gRPC client for Node.js and TypeScript. Circuit breaker, bulkhead, rate limiter, jitter retry, fallback, metrics, and plugins — all in one fluent API.
Measured against a local Express server, Node.js 20 · full results →
| Scenario | Plain axios | super-http | Gain |
|---|---|---|---|
| Connection pool (200 req, 50c) | 2 222 req/s | 4 545 req/s | +105% throughput |
| 50% flaky service (retry) | 51% success | 96% success | +45 pp |
| Circuit breaker during outage | avg 84 ms/req | avg 14 ms/req | −83% latency |
| Bulkhead isolation | p99 = 31 ms | p99 = 25 ms | −19% tail latency |
| Rate limiter (429 avoidance) | 60% 429 errors | 0% 429 errors | zero errors |
| vs. undici (no pool) | — | +105% | auto-pooling beats raw |
import { createClient, ExponentialJitterRetryStrategy, LoggerPlugin } from 'super-http'
const api = createClient({
baseURL: 'https://api.example.com',
preset: 'resilient-api', // sensible defaults in one line
})
// Add-ons — all optional, all composable
api
.use(LoggerPlugin({ prefix: '[checkout]' }))
.on({
onCircuitStateChange: ({ to, failures }) =>
to === 'open' && alerts.send(`Circuit opened after ${failures} failures`),
})
// Per-request policy for non-critical endpoints
const recs = await api.get('/recommendations', {
policy: { timeout: 500, retry: false, fallback: () => [] },
})
// Built-in metrics — no extra setup
const { p99Latency, circuitBreakerTrips, retries } = api.metrics()npm install super-httpRequirements
Node.js ≥ 20 · TypeScript ≥ 5
.proto files super-http/grpc brings the same resilience pipeline to gRPC. Define your service contract in TypeScript and get a fully-typed client with circuit breaker, retry, bulkhead, and metrics — with zero extra dependencies.
import { defineService, unary, serverStream, createGrpcClient, GrpcError } from 'super-http/grpc'
// ① Define service in pure TypeScript — no .proto, no codegen
const UserService = defineService('UserService', {
getUser: unary<{ id: string }, User>(),
listUsers: serverStream<{ active?: boolean }, User>(),
})
// ② Create client with full resilience pipeline
const users = createGrpcClient(UserService, 'grpcs://user-service:443', {
preset: 'resilient-api', // circuit breaker + retry x3 + bulkhead
})
// ③ Unary call — typed response, retry + circuit breaker active
const user = await users.getUser({ id: '42' })
// ④ Server streaming — native AsyncIterable, HTTP/2 backpressure
for await (const u of users.listUsers({ active: true })) {
await processUser(u)
}
// ⑤ Typed error handling by gRPC status code
try {
await users.getUser({ id: 'missing' })
} catch (err) {
if (err instanceof GrpcError && err.code === 'not_found') return null
}Register HTTP and gRPC clients together in the same module. Inject with the same @InjectSuperHttp() decorator — no separate setup needed.
// app.module.ts
@Module({
imports: [
SuperHttpModule.forFeature([
// HTTP client
{
name: 'PAYMENTS',
baseURL: 'https://payments.internal',
preset: 'resilient-api',
},
// gRPC client — same module, same decorator
{
name: 'USER_SVC',
grpc: true,
address: 'user-service.internal:50051',
service: UserServiceDef,
preset: 'resilient-api',
},
]),
],
})
export class AppModule {}// posts.service.ts
@Injectable()
export class PostsService {
constructor(
@InjectSuperHttp('PAYMENTS')
private readonly payments: HttpClient,
@InjectSuperHttp('USER_SVC')
private readonly users: GrpcClient<typeof UserServiceDef>,
) {}
async createPost(dto: CreatePostDto) {
const [author, charge] = await Promise.all([
this.users.getUser({ id: dto.authorId }), // gRPC
this.payments.post('/charges', dto.payment), // HTTP
])
return { ...dto, author, charge }
}
}