Node.js client
Install @hotcodepush/node, create a client with an API token, release a bundle, upload a file, handle errors and retry safely with idempotency keys.
@hotcodepush/node is the typed Node.js client for the REST API: one class, HotCodePush, whose resources mirror the API's paths, so /v1/apps/{appId}/channels is apps.channels. Every endpoint of the Swagger reference is a method with typed options and a typed answer. The CLI and the MCP server are built on it.
The source is on GitHub, MIT.
Install
npm install @hotcodepush/nodeThe package is ES modules only and needs Node.js 22 or later.
Authenticate
import { HotCodePush } from "@hotcodepush/node";
const hotCodePush = new HotCodePush({ token: process.env.HOTCODEPUSH_TOKEN });token is an API token from the console's Settings › API tokens page, sent as Authorization: Bearer on every request. The session token the CLI stores after hotcodepush login works in its place.
| Option | Default | What it is |
|---|---|---|
baseUrl | https://api.hotcodepush.com | Names the API's origin. |
client | node/<the package's version> | Names the client version the API checks, X-HotCodePush-Client. |
token | none | Holds the API token, or a session token. |
userAgent | the runtime's | Sets the User-Agent header, which a session keeps and the console's sessions page shows. |
A method that needs a signed-in session says so in its doc comment. With an API token it throws HotCodePushError with the code forbidden. The hub lists what a token cannot do.
Usage
Every method takes one options object holding the path parameters, the query parameters or the body, and answers what the API answers. A paged list takes limit, offset and, where the API embeds linked resources, relations, and has a count beside it. Wherever the API takes a {userId}, me names the caller.
Release a bundle
import { HotCodePush } from "@hotcodepush/node";
const hotCodePush = new HotCodePush({ token: process.env.HOTCODEPUSH_TOKEN });
const appId = process.env.HOTCODEPUSH_APP_ID;
const channelId = process.env.HOTCODEPUSH_CHANNEL_ID;
const [bundle] = await hotCodePush.apps.bundles.list({ appId, limit: 1 });
const release = await hotCodePush.apps.channels.releases.create({
appId,
channelId,
bundleId: bundle.id,
rolloutPercentage: 10,
});
await hotCodePush.apps.releases.update({ appId, releaseId: release.id, rolloutPercentage: 100 });
await hotCodePush.apps.channels.rollbacks.create({ appId, channelId });A release creates from a ready bundle, or from what another channel serves with fromChannelId in place of bundleId. A rollback is a new release of the channel's previous bundle. Rollouts and auto-pause covers the percentage and the progression. Uploading a bundle is the CLI's job, hotcodepush bundle upload, and the client holds the same calls, apps.files.upload, apps.bundles.create, apps.bundles.pack.upload and apps.bundles.complete, for a pipeline of your own.
Upload a file
import { openAsBlob } from "node:fs";
import { HotCodePush } from "@hotcodepush/node";
const hotCodePush = new HotCodePush({ token: process.env.HOTCODEPUSH_TOKEN });
await hotCodePush.apps.files.upload({
appId: process.env.HOTCODEPUSH_APP_ID,
body: await openAsBlob("dist/index.html.gz"),
sha256: "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08",
});apps.files.upload and apps.bundles.pack.upload stream their body, a Blob or a ReadableStream with its contentLength, and never buffer it. A file goes up gzip-compressed, and sha256 is the hash of its uncompressed content. A Blob above 64 MiB goes up in parts of 10 MiB through the uploads resources, while a stream goes up in one request and is not retried.
Errors
A response outside 2xx throws HotCodePushError, which carries the API's code, message and details verbatim and the HTTP status. A response outside the API's error shape throws the client's own unexpected_response with the status.
import { HotCodePush, HotCodePushError } from "@hotcodepush/node";
const hotCodePush = new HotCodePush({ token: process.env.HOTCODEPUSH_TOKEN });
try {
await hotCodePush.apps.channels.create({ appId: process.env.HOTCODEPUSH_APP_ID, name: "staging" });
} catch (error) {
if (error instanceof HotCodePushError && error.code === "channel_name_taken") {
console.log("staging exists already");
} else {
throw error;
}
}Retries and idempotency
The client retries only where the API makes a repeat safe:
| What the client does | |
|---|---|
| Timeout | An attempt has 60 seconds, an upload 10 minutes. |
| Retries | A GET, DELETE or PUT, and a transition a repeat leaves unchanged, such as pause, resume or a bundle's complete, is sent up to 3 times on a network error, a 408, a 429 or a 5xx, waiting 500 ms and then 1 second, or on a 429 or a 503 what Retry-After asks, up to 60 seconds. A PATCH is never retried. |
| Idempotency | A creating POST sends an Idempotency-Key, a UUID generated per call and kept across its retries, so a retried create answers the first result. |
Pass idempotencyKey to reuse a key when you retry the call yourself. The API answers the first result for 24 hours.
await hotCodePush.apps.channels.create({
appId,
name: "staging",
idempotencyKey: `create-staging-${appId}`,
});organizations.checkouts.create and organizations.customerPortalSessions.create carry no key and are never retried.
Reference
The client covers every endpoint of the API: organizations, members, invitations, apps, channels, bundles, releases, devices, statistics, webhooks and your account. The method list is in the package's README, and each method maps to an operation of the Swagger reference.
Last updated on
API reference
The HotCodePush REST API at api.hotcodepush.com, with API tokens, pagination, relations, idempotency keys, rate limits and the error shape.
Python client
Install hotcodepush, create a client with an API token, release a bundle, upload a file, handle errors and retry safely with idempotency keys.