JS SDK — Node (@loopchat/node)

The server-side SDK for customer backends. It authenticates with the app's api_secret, which must never ship in client apps.

  • Repo: js-sdk/packages/node. Node ≥ 22.12 (uses node:crypto).
  • Dependencies: @loopchat/core (workspace) — for the error class and protocol types only.
  • Build: tsup → ESM + CJS + .d.ts.
import { LoopChatServerClient } from '@loopchat/node';

const server = new LoopChatServerClient(apiKey, apiSecret, { baseUrl: 'https://chat.example.com' });

const token = server.createToken('user-42');            // HS256 user JWT for connectUser
await server.upsertUser({ id: 'user-42', name: 'Ada' });
await server.addMembers('messaging', 'pool-abc', ['user-42'], {
  create_if_missing: { created_by_id: 'user-42' },      // ensureMembership in one call
});

LoopChatServerClient

new LoopChatServerClient(apiKey: string, apiSecret: string, opts?: LoopChatServerClientOptions)

LoopChatServerClientOptions: baseUrl? (default http://localhost:8080), fetchImpl? (injectable for tests).

All requests are server-authenticated with the x-api-key + x-api-secret headers (see API Reference). Non-2xx responses throw LoopChatNetworkError (re-exported from core).

Tokens

Method Description
createToken(userId, { expiresInSec? }) Mints the HS256 user JWT ({ user_id, iat[, exp] }) signed with the app's api_secret — what clients pass to client.connectUser. Omit expiresInSec for a non-expiring token. Local operation, no network call.

Users

Method Endpoint Description
upsertUser(user): Promise<UserObject> POST /users Create or update a chat user ({ id, name?, image?, role?, custom? }).
deleteUser(id): Promise<void> DELETE /users/:id Hard-delete a user (also removes memberships, read state, devices server-side).

Channels

Method Endpoint Description
createChannel(type, input) POST /channels/:type Idempotent create ({ id, name?, members?, created_by_id, custom? }); returns { channel, members }. The creator is always added as a member with role owner.
deleteChannel(type, id, { hard? }) DELETE /channels/:type/:id[?hard=true] Soft delete freezes the channel; hard: true removes it and its data.
addMembers(type, id, members, { create_if_missing? }) POST /channels/:type/:id/members Idempotent — existing members are silently kept. With create_if_missing: { created_by_id, name?, custom? } the channel is created when absent, collapsing the ensureMembership self-heal pattern into one call.
removeMembers(type, id, userIds) DELETE /channels/:type/:id/members/:userId (per user) Removes each user from the channel.