Jetflare is a super lightweight, type-safe HTTP client for TypeScript and JavaScript, featuring:
- 🚀 Type-Safe API - Full TypeScript support with automatic type inference
- ⚡ Real-Time Ready - Built-in WebSocket and SSE support
- 🔄 Smart Caching - Automatic response caching and invalidation
- đź› Developer Experience - IntelliSense for API routes and responses
// Example: Type-safe API client
const api = Jetflare("https://api.example.com", {
GET_user: {
path: "/users/:id",
method: "get",
response: { id: "string", name: "string" },
},
});
// TypeScript knows the response shape
const { data: user } = await api.GET_user({ params: { id: "123" } });
console.log(user.name); // Type-safe property access
# Using npm
npm install jetflare
# Using yarn
yarn add jetflare
# Using pnpm
pnpm add jetflare
- Define your API routes
import { Jetflare } from "jetflare";
const api = Jetflare("https://api.example.com", {
// GET /users
GET_users: {
path: "/users",
method: "get",
response: {
users: [
{
id: "string",
name: "string",
email: "string",
},
],
},
},
// POST /users
POST_user: {
path: "/users",
method: "post",
body: {
name: "string",
email: "string",
},
response: {
id: "string",
name: "string",
email: "string",
},
},
});
- Make type-safe requests
// GET /users
const { data } = await api.GET_users();
// data is typed as { users: Array<{ id: string, name: string, email: string }> }
// POST /users
const newUser = await api.POST_user({
body: { name: "John", email: "john@example.com" },
});
// newUser.data is typed as { id: string, name: string, email: string }
import { createRoutes } from "jetflare";
const routes = createRoutes({
// Define your API routes here
});
GET_product_by_id: { path: "/products/:id", method: "get", params: { id: "string", // Example: string type for path parameter }, title: "Get product by ID", },
// POST request with a request body, invalidates 'GET_products' cache on success CREATE_product: { path: "/products", method: "post", body: { name: "string", price: 0, description: "string", }, invalidates: ["GET_products"], // Invalidate cache for 'GET_products' title: "Create a new product", },
// PUT request with path parameters and body, invalidates specific product cache UPDATE_product: { path: "/products/:id", method: "put", params: { id: "string", }, body: { price: 0, }, invalidates: ["GET_products", "GET_product_by_id"], // Invalidate multiple caches title: "Update a product", },
// DELETE request with path parameters DELETE_product: { path: "/products/:id", method: "delete", params: { id: "string", }, invalidates: ["GET_products"], // Invalidate 'GET_products' cache title: "Delete a product", },
// Example for file upload (handled via payload.body containing File objects) UPLOAD_product_image: { path: "/products/:id/image", method: "post", params: { id: "string", }, body: { imageFile: {} as File, // Explicitly define as File or File[] altText: "string", }, title: "Upload product image", }, });
### WebSocket Routes
Define a WebSocket route by setting the `method` to `"websocket"`.
```typescript
export const routes = createRoutes({
WS_chat: {
path: "/chat",
method: "websocket",
title: "Real-time chat",
},
});
Define an SSE route by setting the method
to "sse"
.
export const routes = createRoutes({
SSE_notifications: {
path: "/notifications",
method: "sse",
query: {
userId: "string",
},
title: "Event stream for notifications",
},
});
Once your jetflare
instance is initialized, you can call your API endpoints directly using the route names defined in your routes
object.
// Example: jetflare.GET_users defined with query: { limit: 0, isActive: false }
const usersResponse = await jetflare.GET_users({
query: {
limit: 10,
isActive: true,
},
});
const usersData = await usersResponse.json();
console.log("Users:", usersData);
// Example: jetflare.GET_product_by_id defined with params: { id: "string" }
const productResponse = await jetflare.GET_product_by_id({
params: { id: "product_123" },
});
const productData = await productResponse.json();
console.log("Product:", productData);
// Example: jetflare.CREATE_product defined with body: { name: "string", price: 0 }
const createResponse = await jetflare.CREATE_product({
body: {
name: "New Widget",
price: 29.99,
description: "A fantastic new product.",
},
});
const newProduct = await createResponse.json();
console.log("Created product:", newProduct);
// Example: jetflare.UPDATE_product defined with params: { id: "string" }, body: { price: 0 }
const updateResponse = await jetflare.UPDATE_product({
params: { id: "product_456" },
body: {
price: 35.5,
},
});
const updatedProduct = await updateResponse.json();
console.log("Updated product:", updatedProduct);
// Example: jetflare.DELETE_product defined with params: { id: "string" }
const deleteResponse = await jetflare.DELETE_product({
params: { id: "product_789" },
});
if (deleteResponse.ok) {
console.log("Product deleted successfully.");
}
Each request accepts an optional payload
object to configure behavior for that specific call:
interface ApiFunctionPayload {
body?: any; // Request body for POST, PUT, PATCH
query?: Record<string, any>; // URL query parameters
params?: Record<string, any>; // URL path parameters (e.g., :id)
headers?: Record<string, string>; // Additional request headers
cache?: boolean | { ttl?: number; key?: string }; // Caching options
timeout?: number; // Request timeout in milliseconds
retry?: boolean | { attempts?: number; delay?: number }; // Retry configuration
onUploadProgress?: (progress: {
loaded: number;
total: number;
percentage: number;
}) => void; // Upload progress callback
onDownloadProgress?: (progress: {
loaded: number;
total: number;
percentage: number;
}) => void; // Download progress callback
signal?: AbortSignal; // For manual request cancellation
}
Jetflare returns an enhanced JetResponse
object, which wraps the standard Response
object and adds useful properties:
const response = await jetflare.GET_users();
// Standard Response properties
console.log(response.ok); // boolean: True if status is 200-299
console.log(response.status); // number: HTTP status code
console.log(response.statusText); // string: HTTP status message
console.log(response.headers); // Headers object
console.log(response.url); // string: Final URL of the response
// Enhanced properties
console.log(response.cached); // boolean: Was this response eligible for caching?
console.log(response.fromCache); // boolean: Did this response come from the cache?
// Response body methods (asynchronous)
const json = await response.json(); // Parses response as JSON
const text = await response.text(); // Parses response as plain text
const blob = await response.blob(); // Parses response as Blob
const buffer = await response.arrayBuffer(); // Parses response as ArrayBuffer
const formData = await response.formData(); // Parses response as FormData
Jetflare simplifies file uploads by automatically converting File
or FileList
objects within your payload.body
into FormData
. Progress tracking is available via onUploadProgress
and onDownloadProgress
.
// Example: jetflare.UPLOAD_product_image defined with body: { imageFile: {} as File, altText: "string" }
// Get a single file from an input element
const fileInput = document.getElementById("imageFile") as HTMLInputElement;
const selectedFile = fileInput.files?.[0];
if (selectedFile) {
try {
const uploadResponse = await jetflare.UPLOAD_product_image({
params: { id: "product_123" },
body: {
imageFile: selectedFile, // Pass the File object directly
altText: "Front view of the product",
},
onUploadProgress: (progress) => {
console.log(`Upload Progress: ${progress.percentage}%`);
// Update a UI progress bar here
},
onDownloadProgress: (progress) => {
console.log(`Download Progress: ${progress.percentage}%`);
},
});
const uploadResult = await uploadResponse.json();
console.log("File upload successful:", uploadResult);
} catch (error) {
console.error("File upload failed:", error);
}
}
// Example for multiple files if your route body supports an array of Files
// Route definition might look like:
// UPLOAD_documents: { path: "/documents", method: "post", body: { files: [] as File[], category: "string" } }
const multipleFilesInput = document.getElementById(
"multipleFiles"
) as HTMLInputElement;
const filesToUpload = multipleFilesInput.files
? Array.from(multipleFilesInput.files)
: [];
if (filesToUpload.length > 0) {
try {
const multiUploadResponse = await jetflare.UPLOAD_documents({
body: {
files: filesToUpload, // Pass an array of File objects
category: "manuals",
},
onUploadProgress: (progress) => {
console.log(`Multi-file Upload Progress: ${progress.percentage}%`);
},
});
const multiUploadResult = await multiUploadResponse.json();
console.log("Multiple files upload successful:", multiUploadResult);
} catch (error) {
console.error("Multiple files upload failed:", error);
}
}
For WebSocket routes, the generated function returns an object with methods to manage the connection, including direct access to the underlying WebSocket
instance.
// Example: jetflare.WS_chat defined with method: "websocket"
const chatService = jetflare.WS_chat();
// Connect to the WebSocket
chatService.connect();
// Get the raw WebSocket instance for advanced usage
const rawSocket = chatService.socket(); // Access the WebSocket object directly
if (rawSocket) {
rawSocket.onopen = () => console.log("WebSocket connected directly!");
rawSocket.onmessage = (event) =>
console.log("Direct WS message:", event.data);
}
// Use Jetflare's convenience methods for event listening
chatService.on("message", (event) => {
console.log("Received message:", JSON.parse(event.data));
});
chatService.on("open", () => {
console.log("WebSocket connection opened!");
chatService.send({ type: "greeting", message: "Hello from Jetflare!" });
});
chatService.on("close", () => {
console.log("WebSocket connection closed.");
});
chatService.on("error", (error) => {
console.error("WebSocket error:", error);
});
// Send data
chatService.send("Plain text message");
chatService.send({ action: "ping", timestamp: Date.now() });
// Close the connection
// chatService.close();
For SSE routes, the generated function returns an object with methods to manage the connection, including direct access to the underlying EventSource
instance.
// Example: jetflare.SSE_notifications defined with method: "sse"
const notificationService = jetflare.SSE_notifications({
query: { userId: "user_abc" },
});
// Connect to the SSE stream
notificationService.connect();
// Get the raw EventSource instance for advanced usage
const rawEventSource = notificationService.event(); // Access the EventSource object directly
if (rawEventSource) {
rawEventSource.onopen = () => console.log("SSE connection opened directly!");
rawEventSource.onerror = (error) => console.error("Direct SSE error:", error);
}
// Use Jetflare's convenience methods for event listening
notificationService.on("message", (event) => {
console.log("New message:", event.data);
});
notificationService.on("user-update", (event) => {
console.log("User updated:", JSON.parse(event.data));
});
// Close the connection
// notificationService.close();
All HTTP requests can accept a payload
object to customize individual request behavior:
const response = await jetflare.GET_users({
headers: { "X-API-Key": "my-secret-key" }, // Custom headers for this request
cache: { ttl: 60000, key: "recent_users" }, // Cache for 1 minute with a specific key
timeout: 8000, // Override global timeout to 8 seconds for this request
retry: { attempts: 3, delay: 500 }, // Retry up to 3 times with 500ms delay between attempts
// onUploadProgress and onDownloadProgress as shown in File Uploads section
// signal for manual cancellation (see Request Cancellation)
});
Jetflare includes an intelligent caching system primarily for GET
requests. It automatically caches responses and can invalidate related cache entries when mutation (POST, PUT, PATCH, DELETE) requests succeed.
// Define caching directly in route configuration (static cache key)
export const routes = createRoutes({
GET_users: {
path: "/users",
method: "get",
cache: { ttl: 300000, key: "all-users-list" }, // Cache for 5 minutes
title: "Get all users with static cache",
},
GET_user_by_id: {
path: "/users/:id",
method: "get",
// Dynamic cache key using path parameters for per-user caching
cache: { ttl: 60000, key: "user-:id" },
title: "Get user by ID with dynamic cache",
},
});
// Override cache behavior per-request
const responseA = await jetflare.GET_users({
cache: { ttl: 30000 }, // Cache this specific request for 30 seconds
});
const responseB = await jetflare.GET_users({
cache: false, // Disable caching for this specific request
});
Define invalidates
in your route configuration to automatically clear relevant cache entries upon successful mutation. Supports specific keys and patterns.
export const routes = createRoutes({
// ...
POST_user: {
path: "/users",
method: "post",
invalidates: ["all-users-list"], // Invalidate the 'all-users-list' cache
title: "Create new user (invalidates user list cache)",
},
PUT_user: {
path: "/users/:id",
method: "put",
// Invalidate the specific user and the overall list
invalidates: ["user-:id", "all-users-list"],
title: "Update user (invalidates user-specific and list cache)",
},
});
You can directly interact with the cache manager through jetflare.cache
.
// Clear all cached entries
jetflare.cache.clear();
// Invalidate specific cache keys or patterns
jetflare.cache.invalidate("all-users-list");
jetflare.cache.invalidate(["user-123", "posts-*"]); // Supports globs with '*'
// Get a cached entry directly
const cachedUsers = jetflare.cache.get("all-users-list");
// Manually set a cache entry
jetflare.cache.set("custom-data", { value: "example" }, 300000); // 5 minutes TTL
Configure automatic retries for failed HTTP requests using the retry
option in your payload.
const response = await jetflare.GET_users({
retry: {
attempts: 5, // Attempt the request up to 5 times (1 initial + 4 retries)
delay: 2000, // Wait 2 seconds between retry attempts
},
timeout: 5000, // Each attempt has its own 5-second timeout
});
If retry
is set to true
, it will default to 1 attempt and 1000ms delay.
Jetflare supports request cancellation using the standard AbortController
API.
const controller = new AbortController();
async function fetchWithCancellation() {
try {
const usersPromise = jetflare.GET_users({ signal: controller.signal });
// Simulate cancelling after some time
setTimeout(() => {
console.log("Aborting request...");
controller.abort();
}, 100);
const response = await usersPromise;
console.log("Request completed:", await response.json());
} catch (error) {
if (error.name === "AbortError") {
console.log("Request successfully aborted.");
} else {
console.error("Request failed:", error);
}
}
}
fetchWithCancellation();
Interceptors allow you to hook into the request and response lifecycle globally to perform actions like logging, authentication, or error handling.
Executed before a request is sent. They receive the payload
configuration and can modify it.
// Add authentication token to all requests
jetflare.interceptors.request.add((config) => {
const token = localStorage.getItem("authToken");
if (token) {
config.headers = {
...config.headers,
Authorization: `Bearer ${token}`,
};
}
return config; // Always return the config object
});
// Add request logging
jetflare.interceptors.request.add((config) => {
console.log(
`Sending request: ${config.route.method.toUpperCase()} ${config.route.path}`
);
return config;
});
Executed after a response is received (for both success and error status codes). They receive the JetResponse
object and can modify it.
// Log response status
jetflare.interceptors.response.add((response) => {
console.log(`Received response: ${response.status} ${response.statusText}`);
return response; // Always return the response object
});
// Handle global refresh tokens (if needed)
jetflare.interceptors.response.add(async (response) => {
if (response.status === 401 && !response.url.includes("/refresh-token")) {
// Logic to refresh token, then potentially retry the original request
console.warn("Authentication failed, attempting token refresh...");
// ... call a refresh token endpoint ...
// Note: Retrying original request within interceptor requires careful handling
}
return response;
});
Executed when a request fails (e.g., network error, timeout, or uncaught HTTP error). They receive the Error
object.
// Global error logging and user notification
jetflare.interceptors.error.add((error) => {
console.error("Jetflare API Error:", error.message);
// Example: Display a toast notification
// showToast("An API error occurred: " + error.message, "error");
return error; // Always re-throw or return the error
});
You can set default headers, timeouts, and the base URL globally for all requests made by your Jetflare instance.
// Set default headers for all HTTP requests
jetflare.setDefaultHeaders({
"Content-Type": "application/json",
"X-Client-ID": "my-app-v1",
});
// Set a global timeout for all requests (e.g., 15 seconds)
jetflare.setDefaultTimeout(15000);
// Update the base URL dynamically
jetflare.setBaseURL("https://new-api.example.com/v2");
Jetflare also provides a fluent API for chaining common configuration methods during initialization or later.
import { Jetflare, createRoutes } from "jetflare";
const baseRoutes = createRoutes({
/* ... your routes ... */
});
const jetflare = Jetflare("https://api.example.com", baseRoutes);
Jetflare handles network errors, timeouts, and non-2xx HTTP responses by throwing errors. You should typically wrap your API calls in try...catch
blocks.
try {
const response = await jetflare.GET_users({ timeout: 2000 }); // Example with timeout
if (!response.ok) {
// Handle specific HTTP error responses (e.g., 404, 500)
const errorData = await response.json().catch(() => null);
throw new Error(
`API Error: ${response.status} ${response.statusText} - ${
errorData?.message || "Unknown error"
}`
);
}
const data = await response.json();
console.log("Data fetched:", data);
} catch (error) {
// Catch network errors, timeouts (AbortError), and custom thrown errors
if (error instanceof Error) {
if (error.name === "AbortError") {
console.error(
"Request aborted (timeout or manual cancellation):",
error.message
);
} else {
console.error("Failed to fetch data:", error.message);
}
} else {
console.error("An unexpected error occurred:", error);
}
}
For global error handling, refer to the Error Interceptors section.
Initializes the Jetflare client.
origin
: The base URL for your API (e.g., "https://www.google.com/url?sa=E\&source=gmail\&q=https://api.example.com").routes
: An object defining your API endpoints.
origin: string
: The current base URL of the API client.cache: CacheManager
: Instance of the cache manager for manual cache operations.wsManager: WebSocketManager
: Instance of the WebSocket manager for direct WebSocket control.interceptors
:request: Set<(config: any) => any>
: Add functions to intercept and modify outgoing request configurations.response: Set<(response: JetResponse) => JetResponse>
: Add functions to intercept and modify incoming responses.error: Set<(error: Error) => Error>
: Add functions to intercept and handle errors.
setBaseURL(url: string): void
: Sets a new base URL for the API client.setDefaultHeaders(headers: Record<string, string>): void
: Sets/merges default headers for all subsequent requests.setDefaultTimeout(timeout: number): void
: Sets a global timeout in milliseconds for all requests.withAuth(token: string): API<routes>
: A fluent method to set a Bearer token in default headers.withTimeout(timeout: number): API<routes>
: A fluent method to set the default timeout.withBaseURL(url: string): API<routes>
: A fluent method to set the base URL.
get(key: string): any | null
: Retrieves data from the cache by key.set(key: string, data: any, ttl?: number): void
: Stores data in the cache with an optional time-to-live (TTL) in milliseconds (defaults to 5 minutes).invalidate(pattern: string | string[]): void
: Invalidates cache entries matching a key or pattern(s). Supports*
for wildcards.clear(): void
: Clears all entries from the cache.
The response object returned by HTTP API calls.
interface JetResponse {
ok: boolean; // True if HTTP status is 200-299
status: number; // HTTP status code (e.g., 200, 404, 500)
statusText: string; // HTTP status message (e.g., "OK", "Not Found")
headers: Headers; // Standard Headers object
url: string; // The URL of the response
cached: boolean; // True if the response was eligible for caching
fromCache: boolean; // True if the response was served from cache
json<T>(): Promise<T>; // Parses the response body as JSON
text(): Promise<string>; // Parses the response body as plain text
blob(): Promise<Blob>; // Parses the response body as a Blob
arrayBuffer(): Promise<ArrayBuffer>; // Parses the response body as an ArrayBuffer
formData(): Promise<FormData>; // Parses the response body as FormData
}
The configuration object passed to individual HTTP API functions.
interface ApiFunctionPayload {
body?: any; // Request body for POST, PUT, PATCH. Supports objects (JSON) or FormData for files.
query?: Record<string, any>; // URL query parameters (e.g., ?limit=10)
params?: Record<string, any>; // URL path parameters (e.g., /users/:id -> { id: '123' })
headers?: Record<string, string>; // Additional headers for this specific request
cache?: boolean | { ttl?: number; key?: string }; // Override caching behavior for this request
timeout?: number; // Override global timeout for this request in milliseconds
retry?: boolean | { attempts?: number; delay?: number }; // Configure retries for this request
onUploadProgress?: (progress: {
// Callback for tracking upload progress
loaded: number;
total: number;
percentage: number;
}) => void;
onDownloadProgress?: (progress: {
// Callback for tracking download progress
loaded: number;
total: number;
percentage: number;
}) => void;
signal?: AbortSignal; // An AbortSignal to manually cancel the request
}
For more comprehensive examples, see the Examples directory in the GitHub repository.
This example demonstrates integrating multiple Jetflare features for a user management service.
import { Jetflare, createRoutes } from "jetflare";
// Define user-related API routes
const userRoutes = createRoutes({
GET_all_users: {
path: "/users",
method: "get",
cache: { ttl: 300000, key: "all-users-list" }, // Cache for 5 mins
title: "Retrieve all users",
},
GET_user_by_id: {
path: "/users/:id",
method: "get",
params: { id: "string" },
cache: { ttl: 60000, key: "user-:id" }, // Cache per user for 1 min
title: "Retrieve user by ID",
},
POST_new_user: {
path: "/users",
method: "post",
body: { name: "string", email: "string", password: "string" },
invalidates: ["all-users-list"], // Invalidate user list cache
title: "Create a new user",
},
PUT_update_user: {
path: "/users/:id",
method: "put",
params: { id: "string" },
body: { name: "string", email: "string" },
invalidates: ["all-users-list", "user-:id"], // Invalidate user list and specific user cache
title: "Update an existing user",
},
DELETE_user: {
path: "/users/:id",
method: "delete",
params: { id: "string" },
invalidates: ["all-users-list", "user-:id"], // Invalidate user list and specific user cache
title: "Delete a user",
},
UPLOAD_user_avatar: {
path: "/users/:id/avatar",
method: "post",
params: { id: "string" },
body: { avatar: {} as File, description: "string" }, // File upload with additional data
invalidates: ["user-:id"], // Invalidate specific user cache
title: "Upload user avatar",
},
});
// Initialize Jetflare instance with authentication and a global timeout
const jetflareApi = Jetflare(
"[https://api.yourapp.com](https://api.yourapp.com)",
userRoutes
)
.withAuth("your-static-jwt-token-here") // Or fetch dynamically and set via interceptor
.withTimeout(20000); // 20 seconds global timeout
// Define a service class for user-related operations
class UserService {
async getAllUsers(page: number = 1, limit: number = 10) {
const response = await jetflareApi.GET_all_users({
query: { page, limit },
});
return response.json();
}
async getUserById(id: string) {
const response = await jetflareApi.GET_user_by_id({ params: { id } });
return response.json();
}
async createUser(data: { name: string; email: string; password?: string }) {
const response = await jetflareApi.POST_new_user({ body: data });
return response.json();
}
async updateUser(id: string, updates: { name?: string; email?: string }) {
const response = await jetflareApi.PUT_update_user({
params: { id },
body: updates,
});
return response.json();
}
async deleteUser(id: string) {
const response = await jetflareApi.DELETE_user({ params: { id } });
if (!response.ok) {
throw new Error(`Failed to delete user: ${response.statusText}`);
}
return response.ok;
}
async uploadUserAvatar(
userId: string,
avatarFile: File,
description: string,
onProgress?: (p: {
loaded: number;
total: number;
percentage: number;
}) => void
) {
const response = await jetflareApi.UPLOAD_user_avatar({
params: { id: userId },
body: { avatar: avatarFile, description },
onUploadProgress: onProgress, // Pass the progress callback
});
return response.json();
}
}
export const userService = new UserService();
// Example Usage (in a React component or utility)
async function runUserExamples() {
try {
// Fetch all users
const users = await userService.getAllUsers(1, 5);
console.log("Fetched users:", users);
// Fetch a specific user
const singleUser = await userService.getUserById("user-abc-123");
console.log("Fetched single user:", singleUser);
// Create a new user
const newUser = await userService.createUser({
name: "Test User",
email: "test@example.com",
password: "securepassword",
});
console.log("Created user:", newUser);
// Update a user
const updatedUser = await userService.updateUser(newUser.id, {
email: "new.test@example.com",
});
console.log("Updated user:", updatedUser);
// Upload an avatar for a user
const dummyFile = new File(["dummy content"], "avatar.png", {
type: "image/png",
});
const uploadResult = await userService.uploadUserAvatar(
newUser.id,
dummyFile,
"Profile picture",
(p) => {
console.log(`Avatar upload: ${p.percentage}%`);
}
);
console.log("Avatar upload result:", uploadResult);
// Delete a user
const deleted = await userService.deleteUser(newUser.id);
console.log("User deleted:", deleted);
} catch (error) {
console.error("User service example failed:", error);
}
}
runUserExamples();
Demonstrates how to use WebSockets for a real-time chat application.
import { Jetflare, createRoutes } from "jetflare";
const chatRoutes = createRoutes({
WS_chat_room: {
path: "/chat/:roomId",
method: "websocket",
title: "Real-time chat room connection",
},
POST_chat_message: {
path: "/chat/:roomId/messages",
method: "post",
params: { roomId: "string" },
body: { message: "string", senderId: "string" },
title: "Send chat message (via REST for history)",
},
});
const jetflareChat = Jetflare("ws://localhost:8080", chatRoutes); // Use ws:// for WebSocket
class ChatService {
private wsConnection: ReturnType<typeof jetflareChat.WS_chat_room> | null =
null;
private currentRoomId: string | null = null;
joinRoom(roomId: string, onMessageReceived: (message: any) => void) {
if (this.wsConnection && this.currentRoomId === roomId) {
console.log(`Already connected to room ${roomId}`);
return;
}
this.leaveRoom(); // Close any existing connection
this.currentRoomId = roomId;
this.wsConnection = jetflareChat.WS_chat_room({ params: { roomId } });
// Connect to the WebSocket
this.wsConnection.connect();
// Set up event handlers
this.wsConnection.on("open", () => {
console.log(`Connected to room ${roomId}`);
// Send a "join" message or other initial handshake
this.wsConnection?.send(JSON.stringify({ type: "join", roomId }));
});
this.wsConnection.on("message", (event: MessageEvent) => {
try {
const data = JSON.parse(event.data);
onMessageReceived(data); // Callback for UI updates
} catch (e) {
console.error("Failed to parse WS message:", e);
}
});
this.wsConnection.on("close", () => {
console.log("Disconnected from chat.");
this.currentRoomId = null;
});
this.wsConnection.on("error", (error: Event) => {
console.error("WebSocket error:", error);
});
}
sendMessage(message: string, senderId: string) {
if (this.wsConnection && this.currentRoomId) {
this.wsConnection.send(
JSON.stringify({
type: "chat",
roomId: this.currentRoomId,
senderId,
content: message,
timestamp: new Date().toISOString(),
})
);
// Also send via REST for message history persistence (optional)
jetflareChat
.POST_chat_message({
params: { roomId: this.currentRoomId },
body: { message, senderId },
})
.catch((err) => console.error("Failed to post message via REST:", err));
} else {
console.warn("Not connected to a chat room.");
}
}
leaveRoom() {
if (this.wsConnection) {
this.wsConnection.close();
this.wsConnection = null;
console.log("Left current chat room.");
}
}
}
const chatService = new ChatService();
// Example Usage:
chatService.joinRoom("general-chat", (message) => {
console.log("New chat message:", message);
// Update your UI with the new message
});
setTimeout(() => {
chatService.sendMessage("Hello everyone!", "user-456");
}, 2000);
setTimeout(() => {
chatService.leaveRoom();
}, 10000);
Create a new API client instance.
| Property | Type | Description |
| ----------- | -------- | ------------------------------------------ | ----- | -------- | ------- | ----------- | ------ | ----------- |
| path
| string
| URL path (supports :param
placeholders) |
| method
| 'get' | 'post' | 'put' | 'delete' | 'patch' | 'websocket' | 'sse'
| HTTP method |
| response?
| object
| Expected response shape for type inference |
| body?
| object
| Request body type definition |
| query?
| object
| Query parameters type definition |
| params?
| object
| Path parameters type definition |
Contributions are welcome! Please see our Contributing Guide for details.
Jetflare is MIT licensed.