SDK Package Selection Guide
SDK Package Selection Guide
Section titled “SDK Package Selection Guide”Overview
Section titled “Overview”UnboundBytes provides two TypeScript/JavaScript SDK packages designed for different use cases:
@unboundbytes/sdk- High-level SDK with domain-specific API methods (Recommended for most users)@unboundbytes/sdk-client- Low-level HTTP client with token refresh and retry logic
This guide helps you choose the right package for your project and understand when to use each.
Quick Comparison
Section titled “Quick Comparison”| Feature | @unboundbytes/sdk | @unboundbytes/sdk-client |
|---|---|---|
| Recommended For | Most applications | Custom integrations, advanced use cases |
| API Methods | ✅ Domain-specific methods (listDevices, createDeployment) | ❌ Generic HTTP methods (get, post, put) |
| Authentication | ✅ Bearer, HMAC, API Key | ⚠️ Manual (via interceptors) |
| Token Refresh | ✅ Automatic (via underlying client) | ✅ Automatic with concurrent request handling |
| Retry Logic | ✅ Exponential backoff | ✅ Exponential backoff with jitter |
| WebSocket Support | ✅ Built-in real-time commands | ❌ Not included |
| Type Safety | ✅ Full TypeScript types from OpenAPI | ✅ Generic type support |
| Request Interceptors | ❌ Not exposed | ✅ Request/Response interceptors |
| Bundle Size | Larger (includes all API methods) | Smaller (HTTP client only) |
| Learning Curve | Lower (intuitive API methods) | Higher (manual request construction) |
| Use Case | Portal, Agent, CLI tools | Custom API clients, middleware |
When to Use @unboundbytes/sdk
Section titled “When to Use @unboundbytes/sdk”Choose @unboundbytes/sdk if you:
✅ Are building a Portal application or Agent ✅ Want ready-to-use API methods without constructing requests manually ✅ Need multiple authentication types (Bearer, HMAC, API Key) ✅ Want WebSocket support for real-time command delivery ✅ Prefer a higher-level abstraction over raw HTTP calls ✅ Are following UnboundBytes best practices for integration
This is the recommended package for 95% of use cases.
Example Use Cases
Section titled “Example Use Cases”- Portal Web Application: User management, deployment dashboard
- Agent Daemon: Device heartbeat, command polling, tunnel management
- CLI Tools: Administrative scripts, deployment automation
- Backend Services: Webhook handlers, background jobs
Code Example
Section titled “Code Example”import { UnboundBytesClient } from '@unboundbytes/sdk';
// Portal: Bearer token authenticationconst client = new UnboundBytesClient({ auth: { type: 'bearer', token: 'your-oidc-token' }});
// High-level API methods (no manual request construction)const devices = await client.listDevices('ten_abc123');const dashboard = await client.getTenantDashboard('ten_abc123');
// Agent: HMAC authenticationconst agentClient = new UnboundBytesClient({ auth: { type: 'hmac', agentId: 'agt_xyz', tenantId: 'ten_abc', sharedSecret: 'your-shared-secret' }});
await agentClient.sendHeartbeat('dev_123', { cpu: 50.0, memory: 4096, disk: 10240, timestamp: new Date().toISOString()});
// WebSocket support for real-time commandsconst ws = agentClient.createWebSocketClient( agentClient.config.auth, 'dev_123', { onCommand: async (command) => { console.log('Received:', command); await agentClient.acknowledgeCommand('dev_123', command.commandId, { status: 'completed' }); } });
ws.connect();When to Use @unboundbytes/sdk-client
Section titled “When to Use @unboundbytes/sdk-client”Choose @unboundbytes/sdk-client if you:
✅ Need fine-grained control over HTTP requests ✅ Are building custom API clients or middleware ✅ Want to minimize bundle size (client only, no API methods) ✅ Need request/response interceptors for custom logic ✅ Are implementing custom authentication flows ✅ Want low-level access to token refresh and retry mechanisms
This is for advanced use cases where the high-level SDK is too opinionated.
Example Use Cases
Section titled “Example Use Cases”- Custom API Gateway: Middleware with custom auth logic
- Third-Party Integrations: Wrapping UnboundBytes API in your own SDK
- Proxy Services: Request forwarding with custom headers
- Testing Infrastructure: Mock servers, request interception
Code Example
Section titled “Code Example”import { ApiClient } from '@unboundbytes/sdk-client';
// Low-level HTTP client with token refreshconst client = new ApiClient({ baseUrl: 'https://api.unboundbytes.com', accessToken: 'your-access-token', refreshToken: 'your-refresh-token', refreshEndpoint: '/api/auth/refresh', timeout: 30000, maxRetries: 3,});
// Manual request construction (generic HTTP methods)const response = await client.get<Device[]>('/v1/devices', { headers: { 'X-Tenant-ID': 'ten_abc123' }});
// Custom request interceptorclient.addRequestInterceptor(async (url, init) => { const headers = new Headers(init.headers); headers.set('X-Custom-Header', 'my-value'); headers.set('X-Request-ID', crypto.randomUUID());
console.log(`Request: ${init.method} ${url}`);
return { url, init: { ...init, headers } };});
// Custom response interceptorclient.addResponseInterceptor(async (response) => { console.log(`Response: ${response.status} from ${response.url}`);
// Custom error handling if (response.status === 429) { const retryAfter = response.headers.get('Retry-After'); throw new Error(`Rate limited. Retry after ${retryAfter}s`); }
return response;});
// Skip token refresh for specific endpointsconst loginResponse = await client.post('/auth/login', { email: 'user@example.com', password: 'password',}, { skipTokenRefresh: true,});
// Skip retry logic for specific requestsconst criticalResponse = await client.get('/api/critical-resource', { skipRetry: true, timeout: 5000,});Architecture: How They Work Together
Section titled “Architecture: How They Work Together”Internally, @unboundbytes/sdk uses @unboundbytes/sdk-client as its underlying HTTP client.
┌─────────────────────────────────────────┐│ @unboundbytes/sdk ││ ┌─────────────────────────────────┐ ││ │ High-Level API Methods │ ││ │ - listDevices() │ ││ │ - createDeployment() │ ││ │ - sendHeartbeat() │ ││ │ - WebSocketClient │ ││ └─────────────┬───────────────────┘ ││ │ ││ ↓ ││ ┌─────────────────────────────────┐ ││ │ @unboundbytes/sdk-client │ ││ │ - Token Refresh │ ││ │ - Retry Logic │ ││ │ - Request Queue │ ││ │ - HTTP Client │ ││ └─────────────────────────────────┘ │└─────────────────────────────────────────┘Key Points:
- If you use
@unboundbytes/sdk, you get both the high-level API methods and the underlying token refresh/retry logic fromsdk-client. - You do not need to install both packages unless you’re building a custom integration.
@unboundbytes/sdkexposessdk-clientfunctionality through its configuration options (timeout, maxRetries, etc.).
Migration Guide
Section titled “Migration Guide”From @unboundbytes/sdk-client to @unboundbytes/sdk
Section titled “From @unboundbytes/sdk-client to @unboundbytes/sdk”If you started with the low-level client and want to migrate to the high-level SDK:
Before (sdk-client):
import { ApiClient } from '@unboundbytes/sdk-client';
const client = new ApiClient({ baseUrl: 'https://api.unboundbytes.com', accessToken: 'your-token', refreshEndpoint: '/api/auth/refresh',});
// Manual request constructionconst devices = await client.get<Device[]>('/v1/devices', { headers: { 'X-Tenant-ID': 'ten_abc123' }});
const newDeployment = await client.post<Deployment>('/v1/deployments', { tenantId: 'ten_abc123', name: 'My Deployment', region: 'us-east-1',});After (sdk):
import { UnboundBytesClient } from '@unboundbytes/sdk';
const client = new UnboundBytesClient({ auth: { type: 'bearer', token: 'your-token' }});
// High-level methods (no manual headers or paths)const devices = await client.listDevices('ten_abc123');
const newDeployment = await client.createDeployment('ten_abc123', { name: 'My Deployment', region: 'us-east-1',});Migration Steps:
- Replace package:
npm install @unboundbytes/sdk(you can remove@unboundbytes/sdk-client) - Update imports: Change
ApiClienttoUnboundBytesClient - Update auth config: Use the
authobject withtypefield - Replace HTTP calls: Use domain-specific methods instead of
get/post - Remove manual headers: Tenant ID and auth headers are handled automatically
- Update error handling: Use SDK-specific error types (
APIError,NetworkError,RateLimitError)
From @unboundbytes/sdk to @unboundbytes/sdk-client
Section titled “From @unboundbytes/sdk to @unboundbytes/sdk-client”If you need to drop down to the low-level client for custom use cases:
Before (sdk):
import { UnboundBytesClient } from '@unboundbytes/sdk';
const client = new UnboundBytesClient({ auth: { type: 'bearer', token: 'your-token' }});
const devices = await client.listDevices('ten_abc123');After (sdk-client):
import { ApiClient } from '@unboundbytes/sdk-client';
const client = new ApiClient({ baseUrl: 'https://api.unboundbytes.com', accessToken: 'your-token', refreshEndpoint: '/api/auth/refresh',});
// Add auth header manuallyclient.addRequestInterceptor(async (url, init) => { const headers = new Headers(init.headers); headers.set('Authorization', `Bearer ${client.getAccessToken()}`); return { url, init: { ...init, headers } };});
const devices = await client.get<Device[]>('/v1/devices', { headers: { 'X-Tenant-ID': 'ten_abc123' }});Migration Steps:
- Install package:
npm install @unboundbytes/sdk-client - Update imports: Change
UnboundBytesClienttoApiClient - Update config: Use
baseUrl,accessToken,refreshEndpointinstead ofauth - Add auth interceptor: Manually add
Authorizationheader via interceptor - Replace method calls: Use
get/post/put/deletewith manual paths - Add manual headers: Include
X-Tenant-IDand other headers as needed
Installation
Section titled “Installation”npm install @unboundbytes/sdknpm install @unboundbytes/sdk-clientFrequently Asked Questions
Section titled “Frequently Asked Questions”Q: Which package should I use for my Portal application?
Section titled “Q: Which package should I use for my Portal application?”A: Use @unboundbytes/sdk. It provides high-level methods like listDevices() and createDeployment() that are perfect for Portal UI development.
Q: Which package should I use for my Agent?
Section titled “Q: Which package should I use for my Agent?”A: Use @unboundbytes/sdk. It supports HMAC authentication and provides methods like sendHeartbeat() and pollCommands() specifically designed for Agent use cases.
Q: When would I ever use @unboundbytes/sdk-client?
Section titled “Q: When would I ever use @unboundbytes/sdk-client?”A: Only when you need:
- Custom request/response interceptors
- Fine-grained control over HTTP requests
- Minimal bundle size (client-only, no API methods)
- Custom authentication flows not supported by the high-level SDK
Q: Can I use both packages in the same project?
Section titled “Q: Can I use both packages in the same project?”A: Yes, but it’s rarely necessary. If you need most of the high-level SDK methods but also need some custom HTTP logic, you can use both. However, this adds bundle size and complexity.
Q: Does @unboundbytes/sdk support token refresh?
Section titled “Q: Does @unboundbytes/sdk support token refresh?”A: Yes! The high-level SDK uses @unboundbytes/sdk-client internally, so you automatically get token refresh, retry logic, and concurrent request handling.
Q: How do I add custom headers with @unboundbytes/sdk?
Section titled “Q: How do I add custom headers with @unboundbytes/sdk?”A: The high-level SDK doesn’t expose interceptors. If you need custom headers, you have two options:
- Use
@unboundbytes/sdk-clientdirectly for fine-grained control - Submit a feature request to add interceptor support to the high-level SDK
Q: What about bundle size?
Section titled “Q: What about bundle size?”A:
@unboundbytes/sdk-client: ~15 KB gzipped (HTTP client only)@unboundbytes/sdk: ~45 KB gzipped (includes all API methods + client)
For most applications, the convenience of the high-level SDK outweighs the bundle size difference.
Q: Can I mix and match? Use the high-level SDK for some calls and low-level for others?
Section titled “Q: Can I mix and match? Use the high-level SDK for some calls and low-level for others?”A: Yes! You can instantiate both clients:
import { UnboundBytesClient } from '@unboundbytes/sdk';import { ApiClient } from '@unboundbytes/sdk-client';
// High-level for most operationsconst sdk = new UnboundBytesClient({ auth: { type: 'bearer', token: 'your-token' }});
// Low-level for custom operationsconst client = new ApiClient({ baseUrl: 'https://api.unboundbytes.com', accessToken: 'your-token',});
// Use high-level for standard operationsconst devices = await sdk.listDevices('ten_abc123');
// Use low-level for custom operationsconst customResponse = await client.get('/v1/custom-endpoint', { headers: { 'X-Custom-Header': 'value' }});Related Documentation
Section titled “Related Documentation”- TypeScript SDK - Full API reference for @unboundbytes/sdk
- Token Refresh Guide - Token refresh implementation details
- API Authentication - Authentication methods and patterns
Summary
Section titled “Summary”| Scenario | Recommended Package |
|---|---|
| Portal web application | @unboundbytes/sdk |
| Agent daemon | @unboundbytes/sdk |
| CLI tools | @unboundbytes/sdk |
| Backend services | @unboundbytes/sdk |
| Custom API gateway | @unboundbytes/sdk-client |
| Third-party SDK wrapper | @unboundbytes/sdk-client |
| Proxy middleware | @unboundbytes/sdk-client |
| Request interception | @unboundbytes/sdk-client |
Default choice: Use @unboundbytes/sdk unless you have a specific need for low-level HTTP control.