Skip to content

Latest commit

 

History

History
373 lines (275 loc) · 8.97 KB

File metadata and controls

373 lines (275 loc) · 8.97 KB

HTTP Client (valdi_http)

The valdi_http module provides a promise-based HTTP client for making network requests from Valdi applications.

Overview

The HTTP client supports all standard HTTP methods (GET, POST, PUT, DELETE, HEAD) and returns cancelable promises, allowing you to abort in-flight requests.

Installation

Add the valdi_http module to your BUILD.bazel dependencies:

valdi_module(
    name = "my_module",
    deps = [
        "@valdi//src/valdi_modules/src/valdi/valdi_http",
    ],
)

Basic Usage

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'
});

API Reference

HTTPClient

The main HTTP client class.

Constructor

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');

Methods

get(pathOrUrl: string, headers?: StringMap<string>): CancelablePromise<HTTPResponse>

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'
});
delete(pathOrUrl: string, headers?: StringMap<string>): CancelablePromise<HTTPResponse>

Performs an HTTP DELETE request.

const response = await client.delete('/api/users/123', {
    'Authorization': 'Bearer token123'
});
head(pathOrUrl: string, headers?: StringMap<string>): CancelablePromise<HTTPResponse>

Performs an HTTP HEAD request (returns only headers, no body).

const response = await client.head('/api/resource');
console.log(response.headers['Content-Length']);

Types

HTTPRequest

interface HTTPRequest {
    url: string;
    method: HTTPMethod;
    headers?: StringMap<string>;
    body?: ArrayBuffer | Uint8Array;
    priority?: number;
}

HTTPResponse

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 map
  • body - Response body as Uint8Array (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);
}

HTTPMethod

enum HTTPMethod {
    GET = 'GET',
    POST = 'POST',
    DELETE = 'DELETE',
    PUT = 'PUT',
    HEAD = 'HEAD',
}

Canceling Requests

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');
}

Working with JSON

Sending JSON

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'
});

Receiving JSON

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);

Error Handling

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;
    }
}

Advanced Usage

Custom Headers

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);

Uploading Binary Data

async function uploadFile(client: HTTPClient, file: Uint8Array) {
    const response = await client.post('/api/upload', file, {
        'Content-Type': 'application/octet-stream'
    });
    
    return response;
}

Request Priority

Some implementations support request prioritization:

const request: HTTPRequest = {
    url: '/api/critical-data',
    method: HTTPMethod.GET,
    headers: { 'Authorization': 'Bearer token' },
    priority: 10 // Higher priority
};

Best Practices

  1. Reuse HTTP client instances: Create one client per base URL and reuse it across your application.

  2. Cancel unnecessary requests: Use the cancelable promise to abort requests when components unmount or data is no longer needed.

  3. Handle errors gracefully: Always check status codes and handle network errors.

  4. Use appropriate timeouts: Cancel long-running requests that exceed expected timeouts.

  5. Decode response bodies: Remember that body is always Uint8Array - decode appropriately for text/JSON.

Integration with Components

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...
    }
}

See Also

Platform Support

The valdi_http module works on:

  • ✅ iOS
  • ✅ Android
  • ✅ Web (via polyfill)

Network requests are always performed asynchronously and will not block the JavaScript thread.