@parcae/sdk
Client SDK for Parcae backends. Pluggable transport layer (Socket.IO or SSE), React provider, and hooks for realtime data fetching.
Install
npm install @parcae/sdk @parcae/model
Create a Client
import { createClient } from "@parcae/sdk";
const client = createClient({ url: "http://localhost:3000" });
const client = createClient({ url: "http://localhost:3000", transport: "sse" });
const client = createClient({ url: "http://localhost:3000", transport: myTransport });
ClientConfig
interface ClientConfig {
url: string;
key?: string | null | (() => Promise<string | null>);
version?: string;
transport?: "socket" | "sse" | Transport;
}
ParcaeClient
createClient() returns a ParcaeClient with the following API:
interface ParcaeClient {
get(path, data?): Promise<any>;
post(path, data?): Promise<any>;
put(path, data?): Promise<any>;
patch(path, data?): Promise<any>;
delete(path, data?): Promise<any>;
subscribe(event, handler): () => void;
unsubscribe(event, handler?): void;
send(event, ...args): void;
readonly isConnected: boolean;
readonly isLoading: boolean;
loading: Promise<void>;
on(event, handler): void;
off(event, handler?): void;
disconnect(): void;
reconnect(): Promise<void>;
setKey(key): Promise<void>;
readonly authVersion: number;
}
The client also calls Model.use(new FrontendAdapter(transport)) automatically, so Model.where(), Model.findById(), and other static methods work immediately after creating a client.
Transports
SocketTransport
Default transport. Socket.IO over WebSocket.
- Bidirectional communication
- Connection pooling (shared socket per url:version)
- Request/response via
"call" event with request IDs
- gzip + compress-json response encoding
- GET request deduplication (in-flight coalescing)
- 30s connection wait timeout for pending requests
- Auth via
"authenticate" event
SSETransport
HTTP + Server-Sent Events. Better for read-heavy workloads or environments where WebSocket is not available.
- Standard
fetch() for request/response
EventSource per subscription channel at /__events/{event}
- Control messages via POST to
/__control
- Bearer token auth via
Authorization header
React
ParcaeProvider
Wrap your app in ParcaeProvider to make the client available to hooks.
import { ParcaeProvider } from "@parcae/sdk/react";
const client = createClient({ url: "http://localhost:3000" });
<ParcaeProvider client={client}>
<App />
</ParcaeProvider>
<ParcaeProvider
url="http://localhost:3000"
apiKey={token}
userId={user.id}
transport="socket"
onReady={(client) => console.log("connected")}
onError={(err) => console.error(err)}
>
<App />
</ParcaeProvider>
Re-authenticates automatically when userId changes. Forwards transport errors via onError.
useQuery
Reactive data fetching with realtime subscriptions. Returns typed model instances that update in place when the server pushes changes.
import { useQuery } from "@parcae/sdk/react";
function PostList() {
const { items, loading, error, refetch } = useQuery(
Post.where({ published: true }).orderBy("createdAt", "desc")
);
if (loading) return <p>Loading...</p>;
if (error) return <p>Error: {error.message}</p>;
return items.map((post) => (
<article key={post.id}>
<h2>{post.title}</h2>
<Suspense fallback="...">
<span>{post.user.name}</span>
</Suspense>
</article>
));
}
How it works:
- On mount, calls
chain.find() to fetch initial data
- Sends
subscribe:query to the server with the serialized query steps
- Server re-evaluates the query when matching models change
- Diff ops (
add, remove, update) are pushed and applied client-side
- Uses
useSyncExternalStore for tear-safe rendering
Caching:
- Global query cache keyed by
modelType:authVersion:steps
- Reference counting with 60s GC timeout after last subscriber unmounts
- Debounced diff-op application (default 100ms)
Pass null or undefined to skip the query:
const { items } = useQuery(userId ? Post.where({ user: userId }) : null);
useApi
Pre-bound HTTP methods from the client.
import { useApi } from "@parcae/sdk/react";
function UploadButton() {
const { post } = useApi();
const upload = async (file) => {
const result = await post("/v1/media/upload", { file });
};
}
useSDK
Raw client instance access.
import { useSDK } from "@parcae/sdk/react";
const client = useSDK();
client.send("some:event", data);
useConnectionStatus
Connection state for the transport.
import { useConnectionStatus } from "@parcae/sdk/react";
function StatusBadge() {
const { isConnected, isLoading } = useConnectionStatus();
if (isLoading) return <span>Connecting...</span>;
return <span>{isConnected ? "Online" : "Offline"}</span>;
}
useSetting
Persistent key-value user settings. GETs on mount, PUTs on update.
import { useSetting } from "@parcae/sdk/react";
function ThemeToggle() {
const [theme, setTheme, { isLoading }] = useSetting("theme", "light");
return (
<button onClick={() => setTheme(theme === "light" ? "dark" : "light")}>
{theme}
</button>
);
}
Exports
import { createClient, SocketTransport, SSETransport } from "@parcae/sdk";
import type { ClientConfig, ParcaeClient, Transport } from "@parcae/sdk";
import {
ParcaeProvider,
useQuery,
useApi,
useSDK,
useConnectionStatus,
useSetting,
useParcae,
} from "@parcae/sdk/react";
import { Model, FrontendAdapter } from "@parcae/sdk";
License
MIT