js-kt-api-client
v3.1.0
Published
Js-katana Api client
Maintainers
Readme
kt-client ⚔️
The Type-Safe bridge between your js-kt server and every JavaScript runtime.
kt-client gives you a drop-in HTTP + WebSocket client that speaks the js-kt runtime language. It pairs a CLI that downloads your server’s live contract and a runtime client that keeps the types in sync while you build.
✨ Why kt-client?
🎯 End-to-End Confidence
- Server-Verified Types: Pull every route, channel, and event signature directly from your running js-kt server.
- Autocomplete Everywhere: Rich IntelliSense in editors, plus inline docs that mirror your server annotations.
- Zero Guesswork: Client method signatures stay locked to your server’s truth source.
⚡ Built for Real Projects
- Unified Transport: HTTP and Socket.IO share the same type layer and configuration.
- Scoped Access: Download only the APIs you are allowed to touch with per-scope filtering.
- Production-Ready Storage: Opt-in caching, token management, and reconnection hooks included.
🧠 Developer Experience Deluxe
- Commander-Powered CLI: Human-friendly commands with smart defaults and helpful prompts.
- Plug & Play: Works with Bun, Node, TypeScript, or plain JavaScript bundles out of the box.
- No Boilerplate: Create a fully configured client with a single function call.
🧱 Architecture at a Glance
kt-client is split into two tightly connected pieces:
| Piece | Path | What it does |
| --- | --- | --- |
| Types Loader CLI | src/client-api-types-loader/index.ts | Downloads live API metadata from your js-kt server and writes it to apiTypes.d.ts. |
| Runtime Client Factory | src/api-client/index.ts → src/index.ts | Builds a typed HTTP + WebSocket client that uses the generated types for autocomplete and safety. |
The default export in src/index.ts is a pre-configured createApiClient that already understands the shapes produced by the loader.
🚀 Quick Start
1. Install
npm install js-kt-api-client
# or
bun add js-kt-api-client2. (Optional) Configure your project
Default js-kt installations need zero setup—the loader assumes http://localhost:3000, /api, and no scope. If your server lives elsewhere, add an apiTypes section to your app’s package.json:
{
"apiTypes": {
"baseUrl": "http://localhost:3000",
"apiPrefix": "/api",
"scope": "dashboard"
}
}baseUrl– js-kt server origin (defaults tohttp://localhost:3000)apiPrefix– HTTP prefix exposed by your server (defaults to/api)scope– optional path prefix to filter routes, channels, and events
3. Pull the latest types
npx kt-client load-types
# or short
npx kt-client lThe CLI will:
- discover your project root
- hit the server’s
__describe-jsonendpoint - generate a fresh
apiTypes.d.tsalongside the package
Need auth? Export
DESCRIPTION_SECRETbefore running the command and the loader will include it automatically.
4. Bootstrap the client
import createClient from "kt-client";
const client = createClient({
baseUrl: "http://localhost:3000",
// adapter defaults to "fetch" for ~2.3× faster raw HTTP calls; pass "axios" to opt in to Axios helpers
// httpPrefix and channellingPrefix default to "/api" and "/channel" respectively
});Autocomplete is now powered by the types you just generated. You can still override prefixes, scopes, or transport preferences at any time.
🔄 Keeping Types Fresh
- Manual refresh: run
npx kt-client load-typeswhenever the server contract changes. - Automation idea: add a post-deploy or postinstall script:
{ "scripts": { "refresh:types": "kt-client load-types", "postinstall": "kt-client load-types" } } - Scoped Projects: use
--scope,--baseUrl, or--apiPrefixflags to override package.json options per run.
🕹️ Runtime Client Overview
The client factory returned by createApiClient gives you three pillars:
1. api – pluggable HTTP adapter
- Choose between the built-in
fetchadapter (default, ~2.3× faster HTTP throughput in our benchmarks) or opt into"axios"if you need its interceptors, transformers, or plugins. - Both adapters expose the same typed
get,post,put,deletehelpers plus caching controls. - With the Axios adapter you still get the underlying axios utilities (
api._get, interceptors, etc.) without rewriting your calls. - Accepts
requestViahints (["http"],["socket"], or both) to force a transport. - Respects caching toggles with
noCaching,httpOnly, andstorageoptions.
2. socket – Socket.IO power-up
asyncEmit(event, body, options)returns typed promises with optional timeouts.on(event, callback)&offmirror the event signatures generated by the loader.- Handles
autoReconnect, scoped namespaces, and before reconnect hooks.
3. Lifecycle helpers
reloadConfig(updatedProps)to swap runtime configuration without rebuilding.close()to tear down socket connections and clear storage when you need the process to exit (ideal for scripts, CLIs, background jobs).createWebStorage()utility for browser-friendly persistence.
🧊 Smart Caching with Web Storage
kt-client ships with a plug-and-play storage helper. Cache responses transparently and choose when to reuse them.
import createClient, { createWebStorage } from "kt-client";
const client = createClient({
baseUrl: "http://localhost:3000",
adapter: "fetch", // default; switch to "axios" to use Axios-specific features
storage: createWebStorage(),
noCaching: () => false // enable caching
});
// Prime the cache (uses socket transport automatically when the socket is connected)
await client.api.get("reports/daily", {
params: { plan: "pro" },
sinceMins: 15 // reuse the cached payload for 15 minutes
});
// Later within 15 minutes, the same request resolves from cache instantly
const cached = await client.api.get("reports/daily", {
params: { plan: "pro" },
sinceMins: 15
});
// Force a fresh fetch even inside the cache window
const fresh = await client.api.get("reports/daily", {
params: { plan: "pro" },
sinceMins: 15,
now: true
});sinceMinschecks storage first; if a response is younger than the window, it’s returned immediately.now: truebypasses the cache for that call and updates the stored response for future requests.
🧪 Example: One Client, Two Transports
Channelling is enabled by default, so the client will opportunistically reuse the socket transport without additional configuration.
import createClient from "kt-client";
const kt = createClient({
baseUrl: "http://localhost:3000",
getToken: () => window.localStorage.getItem("token") ?? undefined
});
// Typed HTTP call via socket if available
const user = await kt.api.get("users/profile", {
params: { includeStats: true }
});
// Typed socket emit that automatically waits for a typed response
const ack = await kt.socket.asyncEmit("chat/message", {
roomId: "general",
message: "Hello from kt-client!"
});
// Force a request over HTTP even when the socket is connected
const dailyReport = await kt.api.get("reports/daily", {
params: { includeMeta: true },
requestVia: ["http"]
});
// Listen to server-side pushes with full type safety
const stop = await kt.socket.on("chat/receive", (payload, respond) => {
console.log(payload.sender, payload.message);
respond?.({ deliveredAt: Date.now() });
});
// Later: unsubscribe & clean up (only required in scripts/services)
await stop();
kt.close();Every string literal route, channel, and event above is validated against the generated types. Autocomplete suggests only valid keys, and payload shapes are inferred automatically. By default, eligible API requests are dispatched over the socket when the server exposes the handler via sockets; if a route is configured with serveVia: ["http"] (or the socket connection is unavailable), kt-client falls back to plain HTTP automatically. Use requestVia: ["http"] to explicitly pin an individual request to HTTP even when the socket is ready.
🧬 Parameter-Aware Routes
The loader inspects your server files and generates literal types that understand dynamic segments. Routes such as users/[id].router.ts are transformed into template-literal safe keys:
const userId = crypto.randomUUID();
const res = await client.api.get(`users/${userId}`, {
params: { verbose: true }
});When you hover over the key, you’ll see it constrained to the shape users/${string}. Editors will even remind you of the expected params, body, headers, and response types automatically.
⚡ Socket-Accelerated HTTP
Channelling is enabled by default, so kt-client routes eligible HTTP requests through your socket connection, cutting latency by avoiding repeat network handshakes.
- Requests fall back to HTTP automatically when sockets are unavailable or when you:
- set
httpOnly: () => trueon the client to enforce HTTP for every request, - set
channelling.useChannellingtofalse(which disables sockets over all), or - pass
{ requestVia: ["http"] }per request/config.
- set
- Combine with caching for blazing-fast UI refreshes while maintaining type guarantees.
If Socket.IO reconnects, kt-client replays pending emits and rehydrates subscriptions without manual work.
🔐 Authentication & Secrets
- Tokens: Provide a
getTokencallback; the client injects the token into both HTTP headers and socket auth payloads. - App headers: Set
appHeaderto send a custom header along every request. - Private docs: Export
DESCRIPTION_SECRETwhen running the loader if your server protects the describe endpoint.
DESCRIPTION_SECRET="super-secret" npx kt-client load-types🧰 Configuration Reference
type ApiProps = {
baseUrl: string;
httpPrefix?: string;
scope?: string;
httpRequestTimeout?: number;
httpOnly?: () => boolean;
noCaching?: () => boolean;
getToken?: () => string | undefined;
appHeader?: string;
storage?: Storage;
onUnauthorized?: () => void | Promise<void>;
adapter?: "fetch" | "axios";
channelling?: {
useChannelling: boolean;
channellingPrefix?: string;
autoConnect?: boolean;
autoReconnect?: boolean;
channellingRequestTimeout?: number;
beforeReconnect?: (options: Partial<ManagerOptions & SocketOptions>) => void | boolean | Promise<void | boolean>;
query?: any;
transports?: string[];
};
};storage: InjectcreateWebStorage()for browser environments or bring your own.onUnauthorized: Handle 401s globally (log out, refresh tokens, etc.).adapter: Stay on the defaultfetchadapter for best raw HTTP performance, or pass"axios"when you need Axios niceties (transformers, interceptors, cancellation). Switching adapters does not change your typed API surface.modifyRequest: Use axios interceptors exposed onclient.apifor custom logic.
🧭 Tips & Best Practices
- Pin your server URL: Use environment variables and feed them to both the loader and the runtime client for consistency.
- Version your types: Regenerate immediately after backend schema changes so reviews catch contract shifts.
- Fail fast in CI: Run
kt-client load-typesduring continuous integration to detect breaking API changes early. - Wrapper ready: Compose kt-client inside your own API service layer—types propagate automatically.
📡 Troubleshooting
| Symptom | Possible Cause | Fix |
| --- | --- | --- |
| No package.json file found | Running CLI outside your project root | Invoke from the app root or pass --baseUrl manually. |
| Secret required | Server has DESCRIPTION_SECRET enforced | Export DESCRIPTION_SECRET before running the loader. |
| Missing autocomplete | Types not referenced by your TS config | Include "./apiTypes.d.ts" in the include array or add /// <reference path="./apiTypes.d.ts" /> to your entry file. |
| Socket stuck reconnecting | TTL expired or scope mismatch | Verify scope, channellingPrefix, and requestVia options. |
🙋 Need Help?
- Check the server companion docs at https://github.com/almoatamed/js-katana for js-kt server features.
- Open an issue or share feedback—kt-client evolves with your workflows.
Happy hacking, and may your types always line up! ⚔️
