Skip to main content

HTTP(S)

HTTP client generator creates type-safe functions for making HTTP requests based on your API specification. It supports various authentication methods, retry logic, and extensibility hooks.

It is currently available through the generators (channels):

This is available through AsyncAPI (requires the HTTP method binding for operations and statusCode for messages) and directly from OpenAPI documents (see From OpenAPI below).

TypeScript​

FeatureIs supported?
Download❌
Upload❌
Retry with backoff✅
OAuth2 Authorization code❌ (browser-only)
OAuth2 Implicit❌ (browser-only)
OAuth2 Password✅
OAuth2 Client Credentials✅
OAuth2 Token Refresh✅
Username/password Authentication✅
Bearer Authentication✅
Basic Authentication✅
API Key Authentication✅
Request/Response Hooks✅
XML Based API❌
JSON Based API✅
POST✅
GET✅
PATCH✅
DELETE✅
PUT✅
HEAD✅
OPTIONS✅

Channels​

Read more about the channels generator here.

Input (AsyncAPI)Using the code
asyncapi: 3.0.0
info:
title: User API
version: 1.0.0
channels:
ping:
address: /ping
messages:
pingRequest:
$ref: '#/components/messages/PingRequest'
pongResponse:
$ref: '#/components/messages/PongResponse'
operations:
postPing:
action: send
channel:
$ref: '#/channels/ping'
bindings:
http:
method: POST
reply:
channel:
$ref: '#/channels/ping'
messages:
- $ref: '#/channels/ping/messages/pongResponse'
components:
messages:
PingRequest:
payload:
type: object
properties:
message:
type: string
PongResponse:
payload:
type: object
properties:
response:
type: string
bindings:
http:
statusCode: 200
// Location depends on the payload generator configurations
import { PingRequest } from './__gen__/payloads/PingRequest';
import { PongResponse } from './__gen__/payloads/PongResponse';
// Location depends on the channel generator configurations
import { http_client } from './__gen__/channels';
const { postPostPing } = http_client;

// Create a request payload
const pingMessage = new PingRequest({ message: 'Hello!' });

// Make a simple request
const response = await postPostPing({
payload: pingMessage,
baseUrl: 'https://api.example.com'
});

// Access the response
console.log(response.data.response); // The deserialized Pong
console.log(response.status); // 200
console.log(response.headers); // Response headers
console.log(response.rawData); // Raw JSON response

From OpenAPI​

The http_client protocol is also generated directly from an OpenAPI document (2.0/3.0/3.1). Each path + method becomes one function. Configure the channels generator with inputType: 'openapi' and protocols: ['http_client'].

Function names come from each operation's operationId (camel-cased). When an operation has no operationId, a name is synthesized from the method and path, e.g. GET /v2/connect/{referenceId} → getV2ConnectReferenceId. Give your operations operationIds for the cleanest client.

As a consumer you work with three generated pieces: the call functions (http_client.ts), the request/response body models (payload/), and the path/query parameter models (parameter/):

import { http_client } from './__gen__/channels';
import { PostV2ConnectRequest } from './__gen__/channels/payload/PostV2ConnectRequest';
import { GetV2ConnectReferenceIdParameters } from './__gen__/channels/parameter/GetV2ConnectReferenceIdParameters';

// Request with a body: build the model, pass it as `payload`.
const created = await http_client.postV2Connect({
baseUrl: 'https://api.example.com',
payload: new PostV2ConnectRequest({ returnUrl: 'https://shop.example/return' })
});
console.log(created.data.connectUrl); // typed response model

// Request with a path parameter: supply it through the parameter model.
const connect = await http_client.getV2ConnectReferenceId({
baseUrl: 'https://api.example.com',
parameters: new GetV2ConnectReferenceIdParameters({ referenceId: 'ref_123' })
});
console.log(connect.data.safepayAccountId);

See the runnable openapi-http-client example for a complete, self-contained setup.

Base URL​

Every generated call accepts an optional baseUrl. The value used at runtime follows this precedence, highest first:

  1. context.baseUrl passed to the call (e.g. getUser({ baseUrl: 'https://api.example.com' })) — always wins.
  2. The document's first HTTP(S) server — when the AsyncAPI servers (or OpenAPI servers) section declares an http/https server, its URL becomes the generated default. Non-HTTP servers (nats, kafka, …), relative URLs, and OpenAPI server URLs whose variables have no default are skipped.
  3. http://localhost:3000 — the fallback when the document declares no usable HTTP(S) server.

So a document with servers: [{ url: 'https://api.example.com' }] generates clients that target https://api.example.com by default, and you only pass baseUrl to override it (for example, to point at a staging environment).

Authentication​

The HTTP client uses a discriminated union for authentication, providing excellent TypeScript autocomplete support.

Bearer Token​

const response = await postPostPing({
payload: message,
baseUrl: 'https://api.example.com',
auth: {
type: 'bearer',
token: 'your-jwt-token'
}
});

Basic Authentication​

const response = await postPostPing({
payload: message,
baseUrl: 'https://api.example.com',
auth: {
type: 'basic',
username: 'user',
password: 'pass'
}
});

API Key​

// API Key in header (default)
const response = await postPostPing({
payload: message,
baseUrl: 'https://api.example.com',
auth: {
type: 'apiKey',
key: 'your-api-key',
name: 'X-API-Key', // Header name (default: 'X-API-Key')
in: 'header' // 'header' or 'query'
}
});

// API Key in query parameter
const response = await postPostPing({
payload: message,
baseUrl: 'https://api.example.com',
auth: {
type: 'apiKey',
key: 'your-api-key',
name: 'api_key',
in: 'query'
}
});

OAuth2 Client Credentials​

For server-to-server authentication:

const response = await postPostPing({
payload: message,
baseUrl: 'https://api.example.com',
auth: {
type: 'oauth2',
flow: 'client_credentials',
clientId: 'your-client-id',
clientSecret: 'your-client-secret',
tokenUrl: 'https://auth.example.com/oauth/token',
scopes: ['read', 'write'],
onTokenRefresh: (tokens) => {
// Called when tokens are obtained/refreshed
console.log('New access token:', tokens.accessToken);
}
}
});

OAuth2 Password Flow​

For legacy applications requiring username/password:

const response = await postPostPing({
payload: message,
baseUrl: 'https://api.example.com',
auth: {
type: 'oauth2',
flow: 'password',
clientId: 'your-client-id',
username: 'user@example.com',
password: 'user-password',
tokenUrl: 'https://auth.example.com/oauth/token',
onTokenRefresh: (tokens) => {
// Store tokens for future use
saveTokens(tokens);
}
}
});

OAuth2 with Pre-obtained Token​

For tokens obtained via browser-based flows (implicit, authorization code):

// Token obtained from browser OAuth flow
const accessToken = getTokenFromBrowserFlow();

const response = await postPostPing({
payload: message,
baseUrl: 'https://api.example.com',
auth: {
type: 'oauth2',
accessToken: accessToken,
refreshToken: refreshToken, // Optional: for auto-refresh on 401
tokenUrl: 'https://auth.example.com/oauth/token',
clientId: 'your-client-id',
onTokenRefresh: (tokens) => {
// Update stored tokens
updateStoredTokens(tokens);
}
}
});

Retry with Exponential Backoff​

Configure automatic retry for failed requests:

const response = await postPostPing({
payload: message,
baseUrl: 'https://api.example.com',
retry: {
maxRetries: 3, // Maximum retry attempts (default: 3)
initialDelayMs: 1000, // Initial delay before first retry (default: 1000)
maxDelayMs: 30000, // Maximum delay between retries (default: 30000)
backoffMultiplier: 2, // Exponential backoff multiplier (default: 2)
retryableStatusCodes: [408, 429, 500, 502, 503, 504], // Status codes to retry
retryOnNetworkError: true, // Retry on network failures
onRetry: (attempt, delay, error) => {
console.log(`Retry attempt ${attempt} after ${delay}ms: ${error.message}`);
}
}
});

Error Handling​

Non-OK HTTP responses throw a typed HttpError instead of returning. HttpError extends the built-in Error and carries the HTTP status, statusText, and the parsed response body:

export class HttpError extends Error {
status: number;
statusText: string;
body?: unknown; // the parsed JSON error body, when present
}

Consume it with an instanceof check:

import { getGetUser, HttpError } from './__gen__/channels/http_client';

try {
const response = await getGetUser({ baseUrl: 'https://api.example.com' });
// response.data is the typed, unmarshalled success payload
} catch (error) {
if (error instanceof HttpError) {
console.error(error.status); // e.g. 404
console.error(error.statusText); // e.g. 'Not Found'
console.error(error.body); // parsed error body (unknown)
}
}

Request/Response Hooks​

Customize request behavior with hooks:

const response = await postPostPing({
payload: message,
baseUrl: 'https://api.example.com',
hooks: {
// Modify request before sending
beforeRequest: async (params) => {
console.log('Making request to:', params.url);
// Add custom header
return {
...params,
headers: {
...params.headers,
'X-Request-ID': generateRequestId()
}
};
},

// Replace the default fetch implementation
makeRequest: async (params) => {
// Use axios, got, or any HTTP client
const axiosResponse = await axios({
url: params.url,
method: params.method,
headers: params.headers,
data: params.body
});
return {
ok: axiosResponse.status >= 200 && axiosResponse.status < 300,
status: axiosResponse.status,
statusText: axiosResponse.statusText,
headers: axiosResponse.headers,
json: () => axiosResponse.data
};
},

// Process response after receiving
afterResponse: async (response, params) => {
console.log(`Response ${response.status} from ${params.url}`);
return response;
},

// Handle errors
onError: async (error, params) => {
console.error(`Request failed: ${error.message}`);
// Optionally transform the error
return error;
}
}
});

Path Parameters​

For operations with path parameters, the generator creates typed parameter classes:

import { UserItemsParameters } from './__gen__/parameters/UserItemsParameters';

// Create parameters with type safety
const params = new UserItemsParameters({
userId: 'user-123',
itemId: 456
});

const response = await getGetUserItem({
baseUrl: 'https://api.example.com',
parameters: params // Replaces {userId} and {itemId} in path
});

Typed Headers​

For operations with defined headers, the generator creates typed header classes:

import { ItemRequestHeaders } from './__gen__/headers/ItemRequestHeaders';

const headers = new ItemRequestHeaders({
xCorrelationId: 'corr-123',
xRequestId: 'req-456'
});

const response = await putUpdateUserItem({
baseUrl: 'https://api.example.com',
parameters: params,
payload: itemData,
requestHeaders: headers // Type-safe headers
});

Additional Headers and Query Parameters​

Add custom headers or query parameters to any request:

const response = await postPostPing({
payload: message,
baseUrl: 'https://api.example.com',
additionalHeaders: {
'X-Custom-Header': 'value',
'Accept-Language': 'en-US'
},
additionalQueryParams: {
include: 'metadata',
format: 'detailed'
}
});

Multi-Status Responses​

For operations that return different payloads based on status code, the generator creates union types:

# AsyncAPI spec with multiple response types
operations:
getItem:
reply:
messages:
- $ref: '#/components/messages/ItemResponse' # 200
- $ref: '#/components/messages/NotFoundError' # 404
const response = await getItemRequest({
baseUrl: 'https://api.example.com',
parameters: params
});

// Response type is union: ItemResponse | NotFoundError
// Use response.status to discriminate
if (response.status === 200) {
console.log('Item:', response.data); // ItemResponse
} else if (response.status === 404) {
console.log('Not found:', response.data); // NotFoundError
}