Communication Protocols
The system supports multiple communication methods between client and server.
Protocol Comparison
| Protocol | Endpoint | Type Safety | Real-time | Use Case |
|---|---|---|---|---|
| REST | /api/* | ❌ Manual | ❌ No | External APIs, simple requests |
| tRPC HTTP | /trpc/* | ✅ Full | ❌ No | Type-safe queries/mutations |
| tRPC WebSocket | /trpc | ✅ Full | ✅ Yes | Real-time sync, subscriptions |
REST API (Hono)
Traditional HTTP endpoints with JSON.
Example: REST Request
// Client-side fetch
const response = await fetch('http://localhost:8787/api/customers');
const customers = await response.json();
// POST with body
await fetch('http://localhost:8787/api/customers', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
ContactName: 'John Doe',
CompanyName: 'ACME Inc',
}),
});
Pros/Cons
| Pros | Cons |
|---|---|
| Universal, works anywhere | No type safety |
| Simple to debug (curl, Postman) | Manual request/response typing |
| Cache-friendly | No real-time |
tRPC HTTP
Type-safe RPC over HTTP.
Example: tRPC HTTP Request
// Using @trpc/client
import { createTRPCClient, httpBatchLink } from '@trpc/client';
import type { AppRouter } from 'cloudflare-d1-worker';
const client = createTRPCClient<AppRouter>({
links: [
httpBatchLink({
url: 'http://localhost:8787/trpc',
}),
],
});
// Full type inference!
const customers = await client.getCustomers.query({});
const result = await client.createCustomer.mutate({
CustomerId: '123',
ContactName: 'John',
CompanyName: 'ACME',
});
Pros/Cons
| Pros | Cons |
|---|---|
| Full type safety | Extra dependency |
| Auto-complete in IDE | Learning curve |
| Validation with Zod | Not universal (JS only) |
tRPC WebSocket
Real-time tRPC over WebSocket.
Why WebSocket?
- Lower latency: No HTTP overhead per request
- Bidirectional: Server can push to client
- Connection reuse: Single connection for all calls
Example: WebSocket tRPC Client
import { createTRPCClient, wsLink } from '@trpc/client';
import type { AppRouter } from 'cloudflare-d1-worker';
const client = createTRPCClient<AppRouter>({
links: [
wsLink({
url: 'ws://localhost:8787/trpc',
}),
],
});
// Same API, but over WebSocket
const result = await client.healthCheck.query();
Server-Side WebSocket Handler
// ws-adapter.ts - Custom JSON-RPC over WebSocket
import { AnyRouter, TRPCError } from '@trpc/server';
interface JSONRPCRequest {
id: number | string;
method: 'query' | 'mutation';
params: {
path: string;
input?: unknown;
};
}
export async function processTRPCMessage(
data: unknown,
router: AnyRouter,
ctx: any
): Promise<string | null> {
const msg = JSON.parse(data as string) as JSONRPCRequest;
// Create a caller and execute the procedure
const caller = router.createCaller(ctx);
const parts = msg.params.path.split('.');
let fn: any = caller;
for (const part of parts) {
fn = fn?.[part];
}
const result = await fn(msg.params.input);
return JSON.stringify({
id: msg.id,
result: { type: 'data', data: result },
});
}
Hono WebSocket Integration
import { upgradeWebSocket } from 'hono/cloudflare-workers';
import { processTRPCMessage } from './ws-adapter';
app.get('/trpc', upgradeWebSocket((c) => ({
async onMessage(event, ws) {
const response = await processTRPCMessage(
event.data,
appRouter,
{ env: c.env }
);
if (response) ws.send(response);
},
onClose: () => console.log('Connection closed'),
})));
Choosing a Protocol
Recommendations
| Scenario | Recommendation |
|---|---|
| External webhook | REST |
| Simple CRUD from other services | REST |
| Internal app with TypeScript | tRPC HTTP |
| Real-time sync | tRPC WebSocket |
| Subscriptions | tRPC WebSocket |