Skip to content

Client SDK

Execute

Execute commands with the client SDK

Execute#

TypeScript
import { SurfClient } from '@surfjs/client'
 
const client = await SurfClient.discover('https://shop.example.com')
 
// Simple execution — throws SurfClientError on failure
const result = await client.execute('search', { query: 'laptop', limit: 5 })
// => { results: [...], total: 42 }

With auth

TypeScript
const client = await SurfClient.discover('https://shop.example.com', {
auth: 'sk-my-token',
})
 
// Auth token is sent automatically on every request
const order = await client.execute('order.create', { sku: 'LAPTOP-01', quantity: 2 })

Updating auth at runtime

TypeScript
// Set or rotate the auth token after construction
client.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:

TypeScript
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

TypeScript
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:

TypeScript
import { SURF_ERROR_CODES, isSurfErrorCode } from '@surfjs/client'
 
// Full list of server-side error codes
console.log(SURF_ERROR_CODES)
// => ['UNKNOWN_COMMAND', 'INVALID_PARAMS', 'AUTH_REQUIRED', ...]
 
// Type-safe runtime check
function 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:

TypeScript
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:

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