MQTT Library

@receiptkit/mqtt-client provides real-time MQTT communication with React hooks, server utilities, and a framework-agnostic client.

When should I use this vs. the REST API?

Use the REST API for simple fire-and-forget print jobs. Use this MQTT library when you need real-time features: printer status monitoring, bridge discovery, live connection state, or high-frequency printing with minimal latency.

Installation

npm
npm install @receiptkit/mqtt-client
pnpm
pnpm add @receiptkit/mqtt-client

Peer dependency: react (optional, only needed for React hooks).

Package Exports

The library has three entry points for different environments:

ImportEnvironmentUse Case
@receiptkit/mqtt-clientAnyCore client, types, topic builders, utilities
@receiptkit/mqtt-client/reactBrowserReact provider, hooks (useReceiptKit, useBridgeStatus, etc.)
@receiptkit/mqtt-client/serverNode.jsServer-side singleton, serverPublish, serverRequestResponse

Quick Start

Browser (Vanilla JS)

TypeScript
import { ReceiptKitClient } from "@receiptkit/mqtt-client";

// Initialize — creates a global singleton
const client = ReceiptKitClient.init({
  apiKey: "rk_pub_your_public_key",
  orgId: "your-org-id",
});

// Connect
await client.connect();

// Send a print job
await client.print("00:11:62:32:5A:2A", {
  templateId: "your-template-id",
  data: {
    storeName: "My Store",
    total: "$9.99",
  },
});

// Check printer status (synchronous — uses cached data)
const status = client.statusCache.getPrinterStatus(
  "bridge-id",
  "00:11:62:32:5A:2A"
);
console.log(status); // { online: true, coverOpen: false, ... }

React

Provider setup (app root)
import { ReceiptKitProvider } from "@receiptkit/mqtt-client/react";

function App() {
  return (
    <ReceiptKitProvider
      apiKey="rk_pub_your_public_key"
      orgId="your-org-id"
    >
      <YourApp />
    </ReceiptKitProvider>
  );
}
Using hooks in a component
import {
  useReceiptKit,
  useBridgeStatus,
  usePrinterStatus,
} from "@receiptkit/mqtt-client/react";

function PrintButton() {
  const { client, connectionStatus } = useReceiptKit();
  const bridge = useBridgeStatus("your-bridge-id");
  const printer = usePrinterStatus("your-bridge-id", "00:11:62:32:5A:2A");

  const handlePrint = async () => {
    await client.print({
      printerId: "00:11:62:32:5A:2A",
      bridgeId: "your-bridge-id",
      templateId: "your-template-id",
      data: { storeName: "My Store", total: "$9.99" },
    });
  };

  return (
    <div>
      <p>MQTT: {connectionStatus}</p>
      <p>Bridge: {bridge?.connectionLevel}</p>
      <p>Printer: {printer?.statusLevel}</p>
      <button onClick={handlePrint} disabled={connectionStatus !== "connected"}>
        Print Receipt
      </button>
    </div>
  );
}

Server (Node.js / API Routes)

TypeScript
import { getServerClient, serverPublish } from "@receiptkit/mqtt-client/server";

// Option 1: Get the shared server client
const client = await getServerClient({
  apiKey: "rk_live_your_secret_key",
  orgId: "your-org-id",
});
await client.print({
  printerId: "00:11:62:32:5A:2A",
  bridgeId: "bridge-abc123",
  templateId: "tmpl-id",
  data: { total: "$9.99" },
});

// Option 2: One-liner publish (using topic builders)
import { createTopicBuilders } from "@receiptkit/mqtt-client";
const topics = createTopicBuilders({ apiKey: "rk_live_...", orgId: "org-id" });
await serverPublish(
  { apiKey: "rk_live_...", orgId: "org-id" },
  topics.print("bridge-abc123"),
  { title: "print-job", printerId: "00:11:62:32:5A:2A", data: { total: "$9.99" } }
);

Client Configuration

OptionTypeDescription
apiKey *stringAPI key for MQTT authentication
orgId *stringOrganization ID (scopes all topics)
endpointstringAWS IoT endpoint (has sensible default)
environment"browser" | "server"Auto-detected. Controls client ID format and connection behavior
autoSubscribeStatusbooleanAuto-subscribe to bridge status on connect (default: true)
keepalivenumberMQTT keepalive in seconds (default: 60)
reconnectPeriodnumberReconnect interval in ms (default: 5000)
connectTimeoutnumberConnection timeout in ms (default: 10000)
debugbooleanEnable debug logging (default: auto-detect based on NODE_ENV)

React Hooks

useReceiptKit()

Access the MQTT client instance and connection status.

const { client, connectionStatus } = useReceiptKit();
// connectionStatus: "disconnected" | "connecting" | "connected" | "error"

useBridgeStatus(bridgeId)

Subscribe to a bridge's real-time status. Returns cached status with live updates.

const bridge = useBridgeStatus("bridge-abc123");
// bridge.connectionLevel: "live" | "stale" | "offline"
// bridge.printers: LivePrinter[]
// bridge.stats: { received, completed, failed }

usePrinterStatus(bridgeId, printerMac)

Subscribe to a specific printer's hardware status (paper, cover, errors).

const printer = usePrinterStatus("bridge-abc123", "00:11:62:32:5A:2A");
// printer.statusLevel: "ready" | "paper-low" | "paper-out" | "cover-open" | ...
// printer.online: boolean

useBridgeDiscover()

Discover all bridges in your organization. Broadcasts a discovery request and collects responses.

const { bridges, isDiscovering, discover } = useBridgeDiscover();
// bridges: Map<string, BridgeStatusResponse>
// Call discover() to trigger a new scan

Topic Builders

All MQTT topics are org-scoped. Use topic builders to generate correct topic strings instead of constructing them manually:

TypeScript
import { createTopicBuilders } from "@receiptkit/mqtt-client";

const topics = createTopicBuilders({ apiKey: "rk_pub_...", orgId: "your-org-id" });

// ── to-bridge (client/server → bridge) ────────────────────────────────
topics.print("bridge-abc123")
// → "receiptkit/org/{orgId}/to-bridge/bridge-abc123/print"

topics.bridgeStatusPoll("bridge-abc123")
// → "receiptkit/org/{orgId}/to-bridge/bridge-abc123/status-poll"

topics.bridgeDiscover()
// → "receiptkit/org/{orgId}/to-bridge/broadcast/discover"

// ── to-client (bridge → client/dashboard) ─────────────────────────────
topics.bridgeStatus("bridge-abc123")
// → "receiptkit/org/{orgId}/to-client/bridge-abc123/status"

topics.bridgePrintResult("bridge-abc123")
// → "receiptkit/org/{orgId}/to-client/bridge-abc123/print-result"

topics.bridgePresence("bridge-abc123")
// → "receiptkit/org/{orgId}/to-client/bridge-abc123/presence"

// ── Wildcard subscriptions ────────────────────────────────────────────
topics.allToClient()
// → "receiptkit/org/{orgId}/to-client/+/+"

topics.allBridgePresence()
// → "receiptkit/org/{orgId}/to-client/+/presence"

Events

The client emits events for connection changes and incoming messages:

TypeScript
const client = ReceiptKitClient.getInstance();

client.on("connectionChange", (status) => {
  console.log("MQTT status:", status);
  // "disconnected" | "connecting" | "connected" | "error"
});

client.on("bridgeStatus", (bridgeId, status) => {
  console.log(`Bridge ${bridgeId}:`, status);
});

client.on("printerStatus", (bridgeId, mac, status) => {
  console.log(`Printer ${mac}:`, status);
});

client.on("bridgePresence", (bridgeId, isOnline) => {
  console.log(`Bridge ${bridgeId} is ${isOnline ? "online" : "offline"}`);
});

Key Concepts

Global Singleton

The client maintains one MQTT connection per page or process. Calling ReceiptKitClient.init() multiple times returns the same instance. This survives React Strict Mode, HMR, and in-app navigation.

StatusCache (Synchronous Reads)

Bridge and printer status is cached in memory. You can read status synchronously via client.statusCache without async MQTT round-trips. The cache is updated automatically when status messages arrive.

Auto-Reconnect

The underlying MQTT.js library handles reconnection automatically with a configurable interval (default: 5 seconds). The client never kills the connection on timeout — it lets MQTT.js keep trying.

Org-Scoped Isolation

All topics are automatically namespaced to the configured orgId. Cross-org messaging is impossible — the MQTT broker (AWS IoT Core) enforces topic-level access control via org-scoped IoT policies.

Related Pages