The valdi_http module provides a promise-based HTTP client for making network requests from Valdi applications.
The HTTP client supports all standard HTTP methods (GET, POST, PUT, DELETE, HEAD) and returns cancelable promises, allowing you to abort in-flight requests.
Add the valdi_http module to your BUILD.bazel dependencies:
valdi_module(
name = "my_module",
deps = [
"@valdi//src/valdi_modules/src/valdi/valdi_http",
],
)import { HTTPClient } from 'valdi_http/src/HTTPClient';
const client = new HTTPClient('https://api.example.com');
// GET request
const response = await client.get('/users');
console.log(response.statusCode); // 200
console.log(response.body); // Uint8Array
// POST request with body
const postData = new TextEncoder().encode(JSON.stringify({ name: 'Alice' }));
const postResponse = await client.post('/users', postData, {
'Content-Type': 'application/json'
});The main HTTP client class.
constructor(baseURL?: string)Creates a new HTTP client with an optional base URL. If provided, all requests will be relative to this base URL.
// With base URL
const client = new HTTPClient('https://api.example.com');
await client.get('/users'); // Requests https://api.example.com/users
// Without base URL (must use full URLs)
const client = new HTTPClient();
await client.get('https://api.example.com/users');Performs an HTTP GET request.
const response = await client.get('/api/data', {
'Authorization': 'Bearer token123',
'Accept': 'application/json'
});post(pathOrUrl: string, body?: ArrayBuffer | Uint8Array, headers?: StringMap<string>): CancelablePromise<HTTPResponse>
Performs an HTTP POST request with optional body.
const jsonData = JSON.stringify({ username: 'alice', email: 'alice@example.com' });
const body = new TextEncoder().encode(jsonData);
const response = await client.post('/api/users', body, {
'Content-Type': 'application/json'
});put(pathOrUrl: string, body?: ArrayBuffer | Uint8Array, headers?: StringMap<string>): CancelablePromise<HTTPResponse>
Performs an HTTP PUT request with optional body.
const updateData = new TextEncoder().encode(JSON.stringify({ name: 'Alice Updated' }));
const response = await client.put('/api/users/123', updateData, {
'Content-Type': 'application/json'
});Performs an HTTP DELETE request.
const response = await client.delete('/api/users/123', {
'Authorization': 'Bearer token123'
});Performs an HTTP HEAD request (returns only headers, no body).
const response = await client.head('/api/resource');
console.log(response.headers['Content-Length']);interface HTTPRequest {
url: string;
method: HTTPMethod;
headers?: StringMap<string>;
body?: ArrayBuffer | Uint8Array;
priority?: number;
}interface HTTPResponse {
headers: StringMap<string>;
statusCode: number;
body?: Uint8Array;
}Response from an HTTP request.
Properties:
statusCode- HTTP status code (200, 404, 500, etc.)headers- Response headers as a string mapbody- Response body asUint8Array(optional)
const response = await client.get('/api/data');
if (response.statusCode === 200) {
// Decode response body
const text = new TextDecoder().decode(response.body);
const data = JSON.parse(text);
console.log(data);
}enum HTTPMethod {
GET = 'GET',
POST = 'POST',
DELETE = 'DELETE',
PUT = 'PUT',
HEAD = 'HEAD',
}All HTTP methods return CancelablePromise, which allows you to cancel in-flight requests:
const request = client.get('/api/large-file');
// Cancel the request after 5 seconds
setTimeout(() => {
request.cancel();
}, 5000);
try {
const response = await request;
console.log('Request completed');
} catch (error) {
console.log('Request was canceled or failed');
}function sendJSON(client: HTTPClient, path: string, data: any): Promise<HTTPResponse> {
const jsonString = JSON.stringify(data);
const body = new TextEncoder().encode(jsonString);
return client.post(path, body, {
'Content-Type': 'application/json'
});
}
const response = await sendJSON(client, '/api/users', {
name: 'Bob',
email: 'bob@example.com'
});async function getJSON<T>(client: HTTPClient, path: string): Promise<T> {
const response = await client.get(path, {
'Accept': 'application/json'
});
if (response.statusCode !== 200) {
throw new Error(`HTTP ${response.statusCode}`);
}
const text = new TextDecoder().decode(response.body);
return JSON.parse(text);
}
interface User {
id: number;
name: string;
email: string;
}
const user = await getJSON<User>(client, '/api/users/123');
console.log(user.name);async function fetchData() {
try {
const response = await client.get('/api/data');
if (response.statusCode >= 200 && response.statusCode < 300) {
// Success
const text = new TextDecoder().decode(response.body);
return JSON.parse(text);
} else {
// HTTP error
throw new Error(`HTTP error: ${response.statusCode}`);
}
} catch (error) {
// Network error or request canceled
console.error('Request failed:', error);
throw error;
}
}const client = new HTTPClient('https://api.example.com');
const commonHeaders = {
'Authorization': 'Bearer my-token',
'X-App-Version': '1.0.0',
'User-Agent': 'MyApp/1.0'
};
const response = await client.get('/api/data', commonHeaders);async function uploadFile(client: HTTPClient, file: Uint8Array) {
const response = await client.post('/api/upload', file, {
'Content-Type': 'application/octet-stream'
});
return response;
}Some implementations support request prioritization:
const request: HTTPRequest = {
url: '/api/critical-data',
method: HTTPMethod.GET,
headers: { 'Authorization': 'Bearer token' },
priority: 10 // Higher priority
};-
Reuse HTTP client instances: Create one client per base URL and reuse it across your application.
-
Cancel unnecessary requests: Use the cancelable promise to abort requests when components unmount or data is no longer needed.
-
Handle errors gracefully: Always check status codes and handle network errors.
-
Use appropriate timeouts: Cancel long-running requests that exceed expected timeouts.
-
Decode response bodies: Remember that
bodyis alwaysUint8Array- decode appropriately for text/JSON.
import { Component } from 'valdi_core/src/Component';
import { HTTPClient } from 'valdi_http/src/HTTPClient';
export class UserListComponent extends Component {
private httpClient = new HTTPClient('https://api.example.com');
private currentRequest?: CancelablePromise<HTTPResponse>;
onCreate() {
this.loadUsers();
}
private async loadUsers() {
try {
this.currentRequest = this.httpClient.get('/api/users');
const response = await this.currentRequest;
const text = new TextDecoder().decode(response.body);
const users = JSON.parse(text);
this.setState({ users });
} catch (error) {
console.error('Failed to load users:', error);
}
}
onDestroy() {
// Cancel any in-flight requests when component is destroyed
if (this.currentRequest) {
this.currentRequest.cancel();
}
}
onRender() {
// Render users...
}
}- Protobuf - For structured binary protocols
- RxJS - For reactive HTTP patterns
- Native Bindings - Type conversion details
- Cancelable Promise - Promise cancellation patterns
The valdi_http module works on:
- ✅ iOS
- ✅ Android
- ✅ Web (via polyfill)
Network requests are always performed asynchronously and will not block the JavaScript thread.