Node.js SDK
The Node.js server SDK calls OpenAPI with your app's credentials to manage users, friends, groups, messages and files, and receives event callbacks and sync callbacks. It takes care of the App Token, retries, rate limits, paging and callback verification. For general rules, see Calling the API; for framework integration, see Receiving callbacks; for every method, see Services and methods.
For building, configuring and running the full scripts and framework examples, testing callbacks end to end and troubleshooting, see the examples guide. There's no chat web UI demo for now.
For a first integration, follow the quickstart to create a user, look it up and send a message in a standalone npm project, without needing access to the source repository.
| You want to | See |
|---|---|
| Install and make one request work | Quickstart |
| Configure the client, token, proxy and shared storage | Client configuration below on this page |
| Timeouts, retries, paging, batches, files and channels | Calling the API |
| Receive event and sync callbacks | Receiving callbacks |
| Look up method signatures, parameters and return types | Services and methods |
| TypeScript and back-end framework examples | Examples guide |
| Fix install, authentication, network or callback errors | FAQ and troubleshooting |
| Check package versions and what was verified | Versions and verification |
Requirements and installation
- Node.js 22.17.0 or later, with Node.js 24 supported. It needs the Node.js runtime: choose
nodejsin Next.js; the Edge runtime isn't supported. - The package is
@deeprespond/im-server-sdk, with no runtime dependencies; it uses the built-infetchand Node.js modules. - It ships both ES modules and CommonJS with TypeScript types, and only named exports.
- Express, Koa, Fastify, Redis and the like are installed by your app as needed; the SDK never pulls them in.
Install the npm package in your project:
npm install @deeprespond/im-server-sdk@1.0.7Or use pnpm add @deeprespond/im-server-sdk@1.0.7. For the current public version and where to install it from, see npm. JavaScript runs as is; TypeScript projects install typescript and @types/node@22 and use moduleResolution: NodeNext, node16 or bundler.
To use local source instead, build a package in sdk/server/node of the source repository:
pnpm install --frozen-lockfile
pnpm build
pnpm pack --pack-destination /tmpThen install the generated .tgz in your project:
npm install /tmp/deeprespond-im-server-sdk-1.0.7.tgzThe package built from source has no examples and is version 1.0.7. It keeps the UNLICENSED license marker; contact DeepRespond about usage rights. After the local source changes, pack and install again.
ES modules and TypeScript:
import { DRClient, isDRError } from '@deeprespond/im-server-sdk';CommonJS:
const { DRClient, isDRError } = require('@deeprespond/im-server-sdk');| Import path | Contents |
|---|---|
@deeprespond/im-server-sdk | DRClient, DRError, BatchError, RawJSON, RAW, parameter and response types |
@deeprespond/im-server-sdk/callback | The callback handler, event types, sync callback results, in-memory stores |
@deeprespond/im-server-sdk/callback/node, /express, /koa, /fastify, /fetch | Callback adapters for frameworks |
@deeprespond/im-server-sdk/redis | Redis stores for the App Token, nonces and event de-duplication |
@deeprespond/im-server-sdk/testing | Testing helpers such as a fake OpenAPI and signed callback requests |
Create a client
Get the app's Client ID and Client Secret from the console. orgName and appName are the parts before and after # in the AppKey.
import { DRClient } from '@deeprespond/im-server-sdk';
export const im = new DRClient({
baseUrl: 'https://api.example.com',
orgName: process.env.IM_ORG_NAME!,
appName: process.env.IM_APP_NAME!,
clientId: process.env.IM_CLIENT_ID!,
clientSecret: process.env.IM_CLIENT_SECRET!,
});One client serves one app; create one per Node.js process or worker_threads worker and reuse it. The constructor only validates parameters, and the App Token is fetched on the first call; every method returns a Promise and can be called concurrently. baseUrl must be an absolute HTTP or HTTPS URL without a query; HTTP is only allowed for the local machine, private networks and hostnames without dots, and public addresses must use HTTPS. Pass the API root, without adding OpenAPI path prefixes yourself.
The Client Secret and App Token are as powerful as the app administrator's password. For clients, use a client SDK such as the Web SDK; never bundle the server SDK into a web page, app or mini program.
Client options
Time options are in milliseconds. An error's retryAfter is in seconds; see Error handling.
| Option | Default | Description |
|---|---|---|
baseUrl | Required | IM API address; a trailing / is removed |
orgName, appName | Required | Tenant ID and app name |
clientId, clientSecret | Required | The app's server credentials |
timeoutMs | 30000 | Total time limit per attempt; completing an upload defaults to 150000 |
maxRetries | 3 | Retries after network errors, timeouts and 5xx, not counting the first attempt; only APIs safe to retry are retried |
maxRateLimitedRetries | 3 | How many times to wait and resend after rate_limited |
maxRetryAfterMs | 60000 | Longest Retry-After to wait automatically; 0 means don't wait |
rateLimit | Off | Local rate limit { perSecond, burst? }; burst defaults to max(1, ceil(perSecond)) |
tokenStore | In-process cache | Store for sharing the App Token; see Sharing the token across processes |
fetch | Built-in fetch | Your own request implementation, (url, init) => Promise<Response> |
hooks | None | beforeRequest and afterResponse; see Logs and troubleshooting |
logger | Silent | { level?, sink? }; with a sink set, the default level is warn |
userAgentSuffix | None | Service name and version appended to the User-Agent |
im.withOptions({ timeoutMs: 3000, maxRetries: 0 }) returns a view that shares the token, connections and rate-limit state. It can override timeoutMs, maxRetries, maxRateLimitedRetries and maxRetryAfterMs, without changing the original client's settings.
First calls
After a user signs in to your system, issue a sign-in ticket for them and pass it to the client SDK:
const result = await im.users.issueLoginTicket('alice', { auto_create: true });
// 在业务接口的响应中将 result.ticket 交给当前已登录的用户。With auto_create true, a missing user is created automatically. For ticket lifetime and usage rules, see Issue a sign-in ticket.
When sending a one-to-one notification, from must be an existing IM user in the app:
await im.messages.send({
from: 'notice',
to_user: 'alice',
client_msg_id: 'order-20261008-paid',
type: 'text',
body: { text: '订单已支付' },
});Pass exactly one of to_user and to_group. Use a stable business ID as client_msg_id so your own retries are de-duplicated too; without one, the SDK generates an ID for this call, reused by automatic retries. See Send messages.
App Token
The SDK fetches, caches and refreshes the App Token automatically, and concurrent requests in one client share a single fetch. When less than 10% of the token's lifetime is left (at least 5 minutes early), it refreshes in the background; when the token has expired or has less than 1 minute left, requests wait for the new one. After a token becomes invalid, it fetches a new one automatically. For what authentication means, see Authentication and App Token.
After rotating the app credentials, call im.updateCredentials(newClientId, newClientSecret), and later requests use the new ones. For troubleshooting, await im.token() returns the currently valid token, fetching one if needed; never log it. im.appId is null before the first fetch, then the app ID.
Sharing the token across processes
import { Redis } from 'ioredis';
import { DRClient } from '@deeprespond/im-server-sdk';
import { ioredisStores } from '@deeprespond/im-server-sdk/redis';
const redis = new Redis(process.env.REDIS_URL!);
const { tokenStore } = ioredisStores(redis, { keyPrefix: 'my-service:im:' });
const im = new DRClient({
baseUrl: 'https://api.example.com',
orgName: process.env.IM_ORG_NAME!,
appName: process.env.IM_APP_NAME!,
clientId: process.env.IM_CLIENT_ID!,
clientSecret: process.env.IM_CLIENT_SECRET!,
tokenStore,
});With node-redis, await redis.connect() first, then use nodeRedisStores(redis). The SDK never closes Redis clients you pass in. Callback nonces and de-duplication can use the same stores; see Shared storage.
Proxies and private certificates
For a proxy, pass your own fetch, such as one using undici's ProxyAgent:
import { ProxyAgent } from 'undici';
import { DRClient } from '@deeprespond/im-server-sdk';
const dispatcher = new ProxyAgent('http://proxy.internal:3128');
const im = new DRClient({
baseUrl: 'https://api.example.com',
orgName: process.env.IM_ORG_NAME!,
appName: process.env.IM_APP_NAME!,
clientId: process.env.IM_CLIENT_ID!,
clientSecret: process.env.IM_CLIENT_SECRET!,
fetch: (url, init) => {
const options: RequestInit = { ...init };
Object.assign(options, { dispatcher });
return globalThis.fetch(url, options);
},
});
// 服务停止时先 await im.close(),再 await dispatcher.close()。Whether the built-in fetch uses proxy environment variables depends on the Node.js version and startup flags; the SDK doesn't read HTTPS_PROXY itself. For a private deployment's CA certificate, set NODE_EXTRA_CA_CERTS before starting Node.js.
When the service stops
await im.close();Closing the original client deals with in-flight calls and stops token refreshing; it can't be called afterward. A view's close() doesn't close the original client. Your own Redis connections and proxy pools are closed by your app.
Channel version
The standalone RTC channel service (im.channels) and types for the 9 channel callbacks are available from 1.0.0. For the APIs, see Channel methods; for usage, see Using standalone channels.
