sockress-client
v0.2.14
Published
Socket-first client SDK for Sockress servers with automatic HTTP fallback and FormData serialization.
Maintainers
Readme
Sockress Client SDK
sockress-client is the companion SDK for Sockress-powered backends. It automatically prefers the WebSocket transport (sharing the same request/response semantics as HTTP) and silently falls back to fetch when a socket is unavailable. Multipart uploads (FormData) are serialized over the wire, so you can use a single API surface for avatars, profile forms, and realtime commands.
Created by Also Coder · GitHub @alsocoders
Features
- Socket-first requests with automatic HTTP fallback
FormData,Blob, and JSON serialization handled automatically- Familiar API:
api.request(),api.get(),api.post(),api.put(),api.patch(),api.delete() - Built-in event emitters for
open,close,error, andreconnect - Works in browsers and Node.js (inject custom
fetch/wsFactory) - Automatic cookie handling (browser only)
- Request queuing when socket is not yet connected
- Automatic reconnection with exponential backoff
Installation
npm install sockress-clientSockress Client supports both ESM and CommonJS:
// ESM
import { sockressClient } from 'sockress-client';
// CommonJS
const { sockressClient } = require('sockress-client');Quick Start
import { sockressClient } from 'sockress-client';
const api = sockressClient({
baseUrl: 'http://localhost:5051',
autoConnect: true,
preferSocket: true
});
// GET request
const users = await api.get('/api/users');
console.log(users.body);
// POST request
const response = await api.post('/api/auth/login', {
body: { email: '[email protected]', password: 'secret' }
});
console.log(response.body.token);API Methods
api.request(options)
Make a request with full control:
const response = await api.request({
path: '/api/users',
method: 'GET',
headers: { 'Authorization': 'Bearer token' },
query: { page: 1, limit: 10 },
body: { name: 'John' },
timeout: 5000,
signal: abortController.signal,
disableHttpFallback: false
});Query parameters (works for both HTTP and Socket)
You can pass query params either in the path or in the query object. Both work for WebSocket and HTTP. If both are used, the query object overrides URL params for conflicting keys:
// URL style
await api.get('/api/users?page=1&limit=10');
// Object style
await api.get('/api/users', { query: { page: 1, limit: 10 } });
// Combined: object overrides URL for same keys
await api.request({
path: '/api/users?page=1&limit=10',
query: { page: 2 } // page becomes 2, limit stays 10
});api.get(path, options?)
const response = await api.get('/api/users', {
query: { page: 1 },
headers: { 'Authorization': 'Bearer token' }
});api.post(path, options?)
const response = await api.post('/api/users', {
body: { name: 'John', email: '[email protected]' }
});api.put(path, options?)
const response = await api.put('/api/users/123', {
body: { name: 'Jane' }
});api.patch(path, options?)
const response = await api.patch('/api/users/123', {
body: { email: '[email protected]' }
});api.delete(path, options?)
const response = await api.delete('/api/users/123');Response Object
All methods return a SockressClientResponse:
interface SockressClientResponse<T> {
status: number; // HTTP status code
ok: boolean; // true if status is 200-299
headers: Record<string, string>; // response headers
body: T; // parsed response body
json: <R = T>() => R; // get body as JSON (type-safe)
text: () => string; // get body as string
raw: () => T; // get raw body
}Example:
const response = await api.get('/api/users');
console.log(response.status); // 200
console.log(response.ok); // true
console.log(response.body); // parsed JSON
console.log(response.json()); // same as body
console.log(response.text()); // JSON stringFile Uploads
Upload files using FormData:
const formData = new FormData();
formData.append('avatar', file);
formData.append('name', 'John Doe');
const response = await api.post('/api/profile/avatar', {
body: formData,
headers: { 'Authorization': 'Bearer token' }
});
console.log(response.body.avatarUrl);The client automatically serializes FormData for socket transport and uses native FormData for HTTP fallback.
Upload Progress Tracking
Track upload progress with the onProgress callback:
const formData = new FormData();
formData.append('video', largeVideoFile);
const response = await api.post('/api/upload', {
body: formData,
onProgress: (progress) => {
console.log(`Uploaded: ${progress.loaded} / ${progress.total} bytes`);
console.log(`Progress: ${progress.percentage}%`);
}
});The onProgress callback receives an object with:
loaded: Number of bytes uploaded so fartotal: Total bytes to upload (may be undefined if size is unknown)percentage: Upload percentage (0-100, undefined if total is unknown)
Note: Progress tracking works for both HTTP (using XMLHttpRequest) and WebSocket (during FormData serialization) transports.
Events
Listen to socket events:
// Socket opened
api.on('open', () => {
console.log('Socket connected');
});
// Socket closed
api.on('close', ({ code, reason }) => {
console.log('Socket closed', code, reason);
});
// Error occurred
api.on('error', (error) => {
console.error('Socket error:', error);
});
// Reconnection attempt
api.on('reconnect', ({ attempt }) => {
console.log('Reconnecting, attempt:', attempt);
});Custom Realtime Events (emit / on)
Send arbitrary realtime events over the WebSocket transport:
// Client -> Server
api.emit('message', 'hello');
api.emit('any_payload', { ok: true, list: [1, 2, 3] });Receive events sent by the server:
api.on('message', (payload) => {
// Note: `message` is also used internally as a legacy channel,
// so you'll usually receive an object like:
// { type: 'event', event: 'message', data: <your data> }
console.log('message event:', payload);
});
api.on('user_typing', (data) => {
console.log('user_typing:', data);
});Server can broadcast events using app.emit('eventName', data).
Remove listeners:
const unsubscribe = api.on('open', () => {
console.log('Connected');
});
// Later
api.off('open', handler);
// or
unsubscribe();Configuration
Create a client with custom options:
const api = sockressClient({
baseUrl: 'http://localhost:5051', // required
socketPath: '/sockress', // WebSocket path (default: '/sockress')
headers: { 'X-Custom': 'value' }, // default headers
timeout: 15_000, // request timeout in ms (default: 15000)
reconnectInterval: 1_000, // initial reconnect delay (default: 1000)
maxReconnectInterval: 15_000, // max reconnect delay (default: 15000)
autoConnect: true, // auto-connect on creation (default: true)
preferSocket: true, // prefer socket over HTTP (default: true)
credentials: 'include', // fetch credentials (default: 'include')
fetchImpl: fetch, // custom fetch implementation
wsFactory: (url) => new WebSocket(url) // custom WebSocket factory
});Node.js Usage
In Node.js, you may need to provide custom implementations:
import fetch from 'node-fetch';
import WebSocket from 'ws';
const api = sockressClient({
baseUrl: 'https://api.example.com',
fetchImpl: fetch as any,
wsFactory: (url) => new WebSocket(url) as any
});Manual Connection Management
Control socket connection manually:
const api = sockressClient({
baseUrl: 'http://localhost:5051',
autoConnect: false // don't auto-connect
});
// Connect manually
await api.connect();
// Close manually
api.close();The client automatically closes the socket on process exit (Node.js) or page unload (browser).
Request Options
path (required)
Request path (e.g., '/api/users')
method (optional)
HTTP method: 'GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'HEAD', 'OPTIONS' (default: 'GET')
headers (optional)
Request headers object
query (optional)
Query parameters object (values can be strings, numbers, booleans, or arrays)
await api.get('/api/users', {
query: {
page: 1,
limit: 10,
tags: ['js', 'ts'],
active: true
}
});body (optional)
Request body. Can be:
- Plain object (serialized as JSON)
FormData(for file uploads)BloborArrayBufferURLSearchParams- String
onProgress (optional)
Callback function to track upload progress. Receives an object with loaded, total, and percentage properties:
onProgress: (progress) => {
console.log(`${progress.percentage}% uploaded`);
}Works for both HTTP and WebSocket transports when uploading files via FormData or Blob.
timeout (optional)
Request timeout in milliseconds (overrides default)
signal (optional)
AbortSignal for canceling requests
disableHttpFallback (optional)
If true, throws an error if socket is unavailable instead of falling back to HTTP (default: false)
Error Handling
Handle errors with try/catch:
try {
const response = await api.post('/api/users', {
body: { name: 'John' }
});
console.log(response.body);
} catch (error) {
console.error('Request failed:', error);
}TypeScript Support
Sockress Client is written in TypeScript and provides full type safety:
interface User {
id: number;
name: string;
email: string;
}
const response = await api.get<User[]>('/api/users');
const users: User[] = response.body;Links
- Website: https://alsocoder.com
- GitHub: https://github.com/alsocoders/sockress
- Issues: https://github.com/alsocoders/sockress/issues
PRs and feedback welcome!
