@debros/network-ts-sdk - TypeScript SDK for DeBros Network
A modern, isomorphic TypeScript SDK for the DeBros Network gateway. Works seamlessly in both Node.js and browser environments with support for database operations, pub/sub messaging, and network management.
Features
- Isomorphic: Works in Node.js and browsers (uses fetch and isomorphic-ws)
- Database ORM-like API: QueryBuilder, Repository pattern, transactions
- Pub/Sub Messaging: WebSocket subscriptions with automatic reconnection
- Authentication: API key and JWT support with automatic token management
- TypeScript First: Full type safety and IntelliSense
- Error Handling: Unified SDKError with HTTP status and code
Installation
npm install @debros/network-ts-sdk
Quick Start
Initialize the Client
import { createClient } from "@debros/network-ts-sdk";
const client = createClient({
baseURL: "http://localhost:6001",
apiKey: "ak_your_api_key:namespace",
});
// Or with JWT
const client = createClient({
baseURL: "http://localhost:6001",
jwt: "your_jwt_token",
});
Database Operations
Create a Table
await client.db.createTable(
"CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT, email TEXT)"
);
Insert Data
const result = await client.db.exec(
"INSERT INTO users (name, email) VALUES (?, ?)",
["Alice", "alice@example.com"]
);
console.log(result.last_insert_id);
Query Data
const users = await client.db.query("SELECT * FROM users WHERE email = ?", [
"alice@example.com",
]);
Using QueryBuilder
const activeUsers = await client.db
.createQueryBuilder("users")
.where("active = ?", [1])
.orderBy("name DESC")
.limit(10)
.getMany();
const firstUser = await client.db
.createQueryBuilder("users")
.where("id = ?", [1])
.getOne();
Using Repository Pattern
interface User {
id?: number;
name: string;
email: string;
}
const repo = client.db.repository<User>("users");
// Find
const users = await repo.find({ active: 1 });
const user = await repo.findOne({ email: "alice@example.com" });
// Save (INSERT or UPDATE)
const newUser: User = { name: "Bob", email: "bob@example.com" };
await repo.save(newUser);
// Remove
await repo.remove(newUser);
Transactions
const results = await client.db.transaction([
{
kind: "exec",
sql: "INSERT INTO users (name, email) VALUES (?, ?)",
args: ["Charlie", "charlie@example.com"],
},
{
kind: "query",
sql: "SELECT COUNT(*) as count FROM users",
args: [],
},
]);
Pub/Sub Messaging
The SDK provides a robust pub/sub client with:
- Multi-subscriber support: Multiple connections can subscribe to the same topic
- Namespace isolation: Topics are scoped to your authenticated namespace
- Server timestamps: Messages preserve server-side timestamps
- Binary-safe: Supports both string and binary (
Uint8Array) payloads - Strict envelope validation: Type-safe message parsing with error handling
Publish a Message
// Publish a string message
await client.pubsub.publish("notifications", "Hello, Network!");
// Publish binary data
const binaryData = new Uint8Array([1, 2, 3, 4]);
await client.pubsub.publish("binary-topic", binaryData);
Subscribe to Topics
const subscription = await client.pubsub.subscribe("notifications", {
onMessage: (msg) => {
console.log("Topic:", msg.topic);
console.log("Data:", msg.data);
console.log("Server timestamp:", new Date(msg.timestamp));
},
onError: (err) => {
console.error("Subscription error:", err);
},
onClose: () => {
console.log("Subscription closed");
},
});
// Later, close the subscription
subscription.close();
Message Interface:
interface Message {
data: string; // Decoded message payload (string)
topic: string; // Topic name
timestamp: number; // Server timestamp in milliseconds
}
Debug Raw Envelopes
For debugging, you can inspect raw message envelopes before decoding:
const subscription = await client.pubsub.subscribe("notifications", {
onMessage: (msg) => {
console.log("Decoded message:", msg.data);
},
onRaw: (envelope) => {
console.log("Raw envelope:", envelope);
// { data: "base64...", timestamp: 1234567890, topic: "notifications" }
},
});
Multi-Subscriber Support
Multiple subscriptions to the same topic are supported. Each receives its own copy of messages:
// First subscriber
const sub1 = await client.pubsub.subscribe("events", {
onMessage: (msg) => console.log("Sub1:", msg.data),
});
// Second subscriber (both receive messages)
const sub2 = await client.pubsub.subscribe("events", {
onMessage: (msg) => console.log("Sub2:", msg.data),
});
// Unsubscribe independently
sub1.close(); // sub2 still active
sub2.close(); // fully unsubscribed
List Topics
const topics = await client.pubsub.topics();
console.log("Active topics:", topics);
Authentication
Switch API Key
client.auth.setApiKey("ak_new_key:namespace");
Switch JWT
client.auth.setJwt("new_jwt_token");
Get Current Token
const token = client.auth.getToken(); // Returns API key or JWT
Get Authentication Info
const info = await client.auth.whoami();
console.log(info.authenticated, info.namespace);
Logout
await client.auth.logout();
Network Operations
Check Health
const healthy = await client.network.health();
Get Network Status
const status = await client.network.status();
console.log(status.healthy, status.peers);
List Peers
const peers = await client.network.peers();
peers.forEach((peer) => {
console.log(peer.id, peer.addresses);
});
Proxy Requests Through Anyone Network
Make anonymous HTTP requests through the Anyone network:
// Simple GET request
const response = await client.network.proxyAnon({
url: "https://api.example.com/data",
method: "GET",
headers: {
Accept: "application/json",
},
});
console.log(response.status_code); // 200
console.log(response.body); // Response data as string
console.log(response.headers); // Response headers
// POST request with body
const postResponse = await client.network.proxyAnon({
url: "https://api.example.com/submit",
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ key: "value" }),
});
// Parse JSON response
const data = JSON.parse(postResponse.body);
Note: The proxy endpoint requires authentication (API key or JWT) and only works when the Anyone relay is running on the gateway server.
Configuration
ClientConfig
interface ClientConfig {
baseURL: string; // Gateway URL
apiKey?: string; // API key (optional, if using JWT instead)
jwt?: string; // JWT token (optional, if using API key instead)
timeout?: number; // Request timeout in ms (default: 30000)
maxRetries?: number; // Max retry attempts (default: 3)
retryDelayMs?: number; // Delay between retries (default: 1000)
storage?: StorageAdapter; // For persisting JWT/API key (default: MemoryStorage)
wsConfig?: Partial<WSClientConfig>; // WebSocket configuration
fetch?: typeof fetch; // Custom fetch implementation
}
Storage Adapters
By default, credentials are stored in memory. For browser apps, use localStorage:
import { createClient, LocalStorageAdapter } from "@debros/network-ts-sdk";
const client = createClient({
baseURL: "http://localhost:6001",
storage: new LocalStorageAdapter(),
apiKey: "ak_your_key:namespace",
});
Error Handling
The SDK throws SDKError for all errors:
import { SDKError } from "@debros/network-ts-sdk";
try {
await client.db.query("SELECT * FROM nonexistent");
} catch (error) {
if (error instanceof SDKError) {
console.log(error.httpStatus); // e.g., 400
console.log(error.code); // e.g., "HTTP_400"
console.log(error.message); // Error message
console.log(error.details); // Full error response
}
}
Browser Usage
The SDK works in browsers with minimal setup:
// Browser example
import { createClient } from "@debros/network-ts-sdk";
const client = createClient({
baseURL: "https://gateway.example.com",
apiKey: "ak_browser_key:my-app",
});
// Use like any other API client
const data = await client.db.query("SELECT * FROM items");
Note: For WebSocket connections in browsers with authentication, ensure your gateway supports either header-based auth or query parameter auth.
Testing
Run E2E tests against a running gateway:
# Set environment variables
export GATEWAY_BASE_URL=http://localhost:6001
export GATEWAY_API_KEY=ak_test_key:default
# Run tests
npm run test:e2e
Examples
See the tests/e2e/ directory for complete examples of:
- Authentication (
auth.test.ts) - Database operations (
db.test.ts) - Transactions (
tx.test.ts) - Pub/Sub messaging (
pubsub.test.ts) - Network operations (
network.test.ts)
Building
npm run build
Output goes to dist/ with ESM and type declarations.
Development
npm run dev # Watch mode
npm run typecheck # Type checking
npm run lint # Linting (if configured)
License
MIT
Support
For issues, questions, or contributions, please open an issue on GitHub or visit DeBros Network Documentation.