Client SDK
Execute
Execute commands with the client SDK
Execute#
import { SurfClient } from '@surfjs/client' const client = await SurfClient.discover('https://shop.example.com') // Simple execution — throws SurfClientError on failureconst result = await client.execute('search', { query: 'laptop', limit: 5 })// => { results: [...], total: 42 }With auth
const client = await SurfClient.discover('https://shop.example.com', { auth: 'sk-my-token',}) // Auth token is sent automatically on every requestconst order = await client.execute('order.create', { sku: 'LAPTOP-01', quantity: 2 })Updating auth at runtime
// Set or rotate the auth token after constructionclient.setAuth('sk-new-token') // Clear auth (go back to unauthenticated)client.setAuth(undefined)SurfClientError#
All failures throw SurfClientError — never a raw Error. Catch it to handle errors programmatically:
import { SurfClient, SurfClientError } from '@surfjs/client' try { await client.execute('admin.reset', {})} catch (err) { if (err instanceof SurfClientError) { console.log(err.code) // 'AUTH_REQUIRED' console.log(err.message) // 'Surf error [AUTH_REQUIRED]: ...' console.log(err.statusCode) // 401 (HTTP status, or undefined for network errors) console.log(err.retryAfter) // seconds to wait (for RATE_LIMITED only) }}Properties
| Property | Type | Description |
|----------|------|-------------|
| code | SurfClientErrorCode | Machine-readable error code |
| message | string | Human-readable description |
| statusCode | number? | HTTP status code, if the error came from the server |
| retryAfter | number? | Seconds before retry is safe (only set for RATE_LIMITED) |
| name | string | Always 'SurfClientError' |
Error Codes
SurfClientErrorCode is a union of server-side SurfErrorCode values and additional client-side codes:
Server errors (from @surfjs/core SurfErrorCode):
| Code | HTTP | Description |
|------|------|-------------|
| UNKNOWN_COMMAND | 404 | Command not registered on the server |
| INVALID_PARAMS | 400 | Missing required field or wrong type |
| AUTH_REQUIRED | 401 | Command requires auth but none was sent |
| AUTH_FAILED | 403 | Token present but invalid or expired |
| SESSION_EXPIRED | 410 | Session no longer valid |
| RATE_LIMITED | 429 | Too many requests — check retryAfter |
| INTERNAL_ERROR | 500 | Unexpected server failure |
| NOT_SUPPORTED | 501 | Feature not available in this environment |
| NOT_FOUND | 404 | Domain resource not found |
Client-side codes (no HTTP status):
| Code | Description |
|------|-------------|
| NETWORK_ERROR | Request failed — DNS, TCP, or fetch error |
| TIMEOUT | Request exceeded the configured timeout |
| NOT_CONNECTED | WebSocket transport not connected |
| INVALID_MANIFEST | Manifest could not be parsed or is missing required fields |
| MAX_RETRIES | Retry budget exhausted after transient failures |
| HTTP_ERROR | Non-Surf HTTP error (e.g. reverse proxy returned 502) |
Handling by code
try { await client.execute('order.create', params)} catch (err) { if (!(err instanceof SurfClientError)) throw err // rethrow unexpected errors switch (err.code) { case 'AUTH_REQUIRED': redirectToLogin() break case 'AUTH_FAILED': showMessage('Session expired — please log in again') break case 'RATE_LIMITED': scheduleRetry(err.retryAfter ?? 30) break case 'INVALID_PARAMS': showValidationError(err.message) break case 'NETWORK_ERROR': case 'TIMEOUT': showMessage('Connection issue — check your network') break default: reportError(err) }}SURF_ERROR_CODES and isSurfErrorCode
Utilities for working with error codes at runtime:
import { SURF_ERROR_CODES, isSurfErrorCode } from '@surfjs/client' // Full list of server-side error codesconsole.log(SURF_ERROR_CODES)// => ['UNKNOWN_COMMAND', 'INVALID_PARAMS', 'AUTH_REQUIRED', ...] // Type-safe runtime checkfunction handleCode(code: string) { if (isSurfErrorCode(code)) { // code is narrowed to SurfErrorCode here console.log('Server error:', code) }}isSurfErrorCode is useful when building middleware, logging pipelines, or custom error mappers that receive code as a string from the wire.
Retry Configuration#
The client automatically retries transient failures (network errors, 5xx responses) with exponential backoff. Configure via SurfClientOptions:
const client = await SurfClient.discover('https://shop.example.com', { retry: { maxAttempts: 3, // default: 3 backoffMs: 500, // default: 500ms backoffMultiplier: 2, // default: 2 — exponential backoff retryOn: [429, 502, 503, 504], // HTTP status codes to retry on },})Permanent errors (AUTH_REQUIRED, AUTH_FAILED, INVALID_PARAMS, UNKNOWN_COMMAND, FORBIDDEN, NOT_FOUND) are never retried — only transient errors are.
checkForUpdates
Poll for manifest changes to detect API drift:
const update = await client.checkForUpdates() if (update.changed) { console.log('Manifest changed — new checksum:', update.checksum) // Re-initialize your typed client or prompt the user to refresh}Returns { changed: boolean; checksum: string; manifest?: SurfManifest }. The client compares the live checksum against the cached manifest; if they differ, changed is true and the fresh manifest is included.