@smarthivelabs-devs/hive-socket-node
v1.1.0
Published
Node.js SDK for Hive-Socket — push notifications, events, and real-time messages to connected clients
Downloads
119
Readme
@smarthivelabs-devs/hive-socket-node
Node.js SDK for Hive-Socket — push real-time notifications and broadcast typed messages to connected clients from any backend service.
v1.1.0 adds
hive.broadcast.toRoom()for sending structured typed messages received byuseMessages()on the client. Use this for full-duplex features (live competitions, collaborative editing, presence) where clients need to act on the payload, not just display a notification.
Install
npm install @smarthivelabs-devs/hive-socket-node
# or
yarn add @smarthivelabs-devs/hive-socket-node
# or
pnpm add @smarthivelabs-devs/hive-socket-nodeRequirements: Node 18+ (uses native fetch). No peer dependencies.
Supports both ESM and CommonJS:
// ESM
import { createHiveSocketClient } from '@smarthivelabs-devs/hive-socket-node';
// CommonJS
const { createHiveSocketClient } = require('@smarthivelabs-devs/hive-socket-node');Environment variables
Add to your backend .env:
HIVE_SOCKET_URL=https://socket.smarthivelabs.dev
HIVE_SOCKET_API_KEY=shai_<your-key> # same value as INTERNAL_API_KEY on the Hive-Socket server
SMARTHIVE_PROJECT_ID=<your-auth-project-id> # project ID from SmartHive Auth dashboardHIVE_SOCKET_API_KEY — copy the value of INTERNAL_API_KEY set on the Hive-Socket server. This is a shared secret between your backends and Hive-Socket.
SMARTHIVE_PROJECT_ID — the project ID issued by SmartHive Auth when you registered your product. Pass this as projectId in every notify call so Hive-Socket scopes messages and notifications to the correct project.
Quick start
import { createHiveSocketClient } from '@smarthivelabs-devs/hive-socket-node';
const hive = createHiveSocketClient({
baseUrl: process.env.HIVE_SOCKET_URL!,
apiKey: process.env.HIVE_SOCKET_API_KEY!,
});
// Push a notification to a user
await hive.notify.user('user-id-123', {
projectId: process.env.SMARTHIVE_PROJECT_ID!,
title: 'New message',
body: 'Alice sent you a message',
type: 'message',
});API
createHiveSocketClient(config)
Creates a client instance. Create once and reuse across your application.
const hive = createHiveSocketClient({
baseUrl: process.env.HIVE_SOCKET_URL!, // e.g. https://socket.smarthivelabs.dev
apiKey: process.env.HIVE_SOCKET_API_KEY!, // shai_* internal API key
timeout: 10_000, // Optional. Request timeout in ms. Default: 10000
});hive.notify.user(userId, payload)
Push a notification to a specific user. Delivered instantly to all their connected devices. If the user is offline, the notification is persisted and delivered on their next login.
await hive.notify.user(userId, {
projectId: string, // SmartHive Auth project ID (process.env.SMARTHIVE_PROJECT_ID)
title: string, // Notification title
body: string, // Notification body
type: NotificationType, // 'info' | 'success' | 'warning' | 'error' | 'message'
metadata?: Record<string, unknown>, // Optional — any JSON data
sourceService?: string, // Optional — identifies the sending service
});Example — payment confirmed:
await hive.notify.user(userId, {
projectId: process.env.SMARTHIVE_PROJECT_ID!,
title: 'Payment confirmed',
body: `₦${amount.toLocaleString()} received`,
type: 'success',
metadata: { paymentId, amount },
sourceService: 'payment-backend',
});Example — new comment:
await hive.notify.user(comment.authorId, {
projectId: workspace.projectId,
title: 'New reply',
body: `${commenter.name} replied to your comment`,
type: 'message',
metadata: { commentId: comment.id, threadId: comment.threadId },
sourceService: 'workspace-backend',
});hive.notify.room(roomId, payload)
Broadcast a notification to all users currently in a room. Room notifications are ephemeral — not persisted to the database.
await hive.notify.room(roomId, {
projectId: string, // SmartHive Auth project ID (process.env.SMARTHIVE_PROJECT_ID)
title: string,
body: string,
type: NotificationType,
metadata?: Record<string, unknown>,
sourceService?: string,
});Example — quiz started:
await hive.notify.room(`course:${courseId}`, {
projectId: process.env.SMARTHIVE_PROJECT_ID!,
title: 'Quiz started',
body: 'Chapter 3 quiz is now live',
type: 'info',
sourceService: 'hivedemia-backend',
});Example — live vote update:
await hive.notify.room(`event:${eventId}`, {
projectId: process.env.SMARTHIVE_PROJECT_ID!,
title: 'Results updated',
body: 'New votes have been counted',
type: 'info',
metadata: { candidateId, totalVotes },
sourceService: 'votyhive-backend',
});hive.broadcast.toRoom(roomId, payload) (v1.1.0)
Broadcast a typed structured message to all sockets in a room. Messages are received by clients via useMessages(roomId) — they arrive as message:new events, distinct from notification:new produced by notify.*. Use this for real-time game state, collaborative state changes, or any payload where the client needs to react to the message type programmatically.
await hive.broadcast.toRoom(roomId, {
projectId: string, // SmartHive Auth project ID
type: string, // your event type, e.g. "competition.question-change"
payload: Record<string, unknown>, // arbitrary JSON
senderId?: string, // optional — defaults to "system"
});Example — competition state advance:
await hive.broadcast.toRoom(`competition:${competitionId}`, {
projectId: process.env.SMARTHIVE_PROJECT_ID!,
type: 'competition.question-change',
payload: { questionIndex: 2, questionEndsAt: '2026-05-24T12:00:30.000Z' },
senderId: 'hivedemia-api',
});Difference between notify.room and broadcast.toRoom:
| | notify.room | broadcast.toRoom |
|---|---|---|
| Client hook | useNotifications() | useMessages(roomId) |
| Wire event | notification:new | message:new |
| Persisted | Yes (offline delivery) | No (ephemeral) |
| Best for | In-app alerts, toasts | Game state, live sync |
hive.health()
Check server health and current connection count.
const { status, connections, uptime } = await hive.health();
// { status: 'ok', connections: 42, uptime: 3600 }Error handling
import {
HiveSocketAuthError,
HiveSocketNetworkError,
HiveSocketError,
} from '@smarthivelabs-devs/hive-socket-node';
try {
await hive.notify.user(userId, payload);
} catch (err) {
if (err instanceof HiveSocketAuthError) {
// HTTP 401 — wrong API key
// Check HIVE_SOCKET_API_KEY matches INTERNAL_API_KEY on the server
} else if (err instanceof HiveSocketNetworkError) {
// Network failure or request timed out
// Safe to retry with exponential backoff
} else if (err instanceof HiveSocketError) {
// Other server error
console.error(err.message, err.statusCode);
}
}NestJS integration
Create a shared module
// hive-socket.module.ts
import { Module, Global } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { createHiveSocketClient, HiveSocketClient } from '@smarthivelabs-devs/hive-socket-node';
export const HIVE_SOCKET = Symbol('HIVE_SOCKET');
@Global()
@Module({
providers: [
{
provide: HIVE_SOCKET,
inject: [ConfigService],
useFactory: (config: ConfigService) =>
createHiveSocketClient({
baseUrl: config.getOrThrow('HIVE_SOCKET_URL'),
apiKey: config.getOrThrow('HIVE_SOCKET_API_KEY'),
}),
},
],
exports: [HIVE_SOCKET],
})
export class HiveSocketModule {}Inject into any service
// notifications.service.ts
import { Injectable, Inject } from '@nestjs/common';
import { HiveSocketClient } from '@smarthivelabs-devs/hive-socket-node';
import { HIVE_SOCKET } from './hive-socket.module';
@Injectable()
export class NotificationsService {
constructor(
@Inject(HIVE_SOCKET) private readonly hive: HiveSocketClient,
) {}
async sendPaymentNotification(userId: string, projectId: string, amount: number) {
await this.hive.notify.user(userId, {
projectId,
title: 'Payment received',
body: `Your payment of ₦${amount.toLocaleString()} was confirmed`,
type: 'success',
metadata: { amount },
sourceService: 'payment-service',
});
}
async announceToRoom(roomId: string, projectId: string, message: string) {
await this.hive.notify.room(roomId, {
projectId,
title: 'Announcement',
body: message,
type: 'info',
sourceService: 'admin-service',
});
}
}Express / Next.js API route
Express
// routes/messages.ts
import { Router } from 'express';
import { hive } from '../lib/hive-socket'; // your singleton
const router = Router();
router.post('/messages', async (req, res) => {
const { userId, projectId, text } = req.body;
// ... save message to your DB ...
await hive.notify.user(userId, {
projectId,
title: 'New message',
body: text.slice(0, 100),
type: 'message',
sourceService: 'workspace-api',
});
res.json({ ok: true });
});Next.js App Router
// app/api/notify/route.ts
import { createHiveSocketClient } from '@smarthivelabs-devs/hive-socket-node';
const hive = createHiveSocketClient({
baseUrl: process.env.HIVE_SOCKET_URL!,
apiKey: process.env.HIVE_SOCKET_API_KEY!,
});
export async function POST(req: Request) {
const { userId, projectId, title, body } = await req.json();
await hive.notify.user(userId, {
projectId,
title,
body,
type: 'info',
});
return Response.json({ ok: true });
}Notification types
| Type | When to use |
|------|-------------|
| 'message' | New chat message or direct message |
| 'info' | General information (quiz started, event begins) |
| 'success' | Positive outcome (payment confirmed, grade posted) |
| 'warning' | Requires attention (deadline approaching, low balance) |
| 'error' | Something failed (payment declined, submission error) |
Security
How the API key is sent and validated
Every request from this SDK includes an x-internal-api-key header:
POST /notify/user
x-internal-api-key: shai_<your-key>The Hive-Socket server compares this header against its INTERNAL_API_KEY env var using a constant-time comparison (no timing attacks). If the key doesn't match the request is rejected with HTTP 401.
This key is a shared secret between your backends and Hive-Socket. It must never be used in browser or mobile code — it gives unrestricted ability to send notifications to any user.
What the server enforces
| Guarantee | How |
|-----------|-----|
| Only trusted backends can push notifications | x-internal-api-key validated on every request |
| Notifications are scoped to the correct project | projectId from your payload scopes DB storage and room routing |
| userId comes from your backend, not the client | Clients never call notify endpoints — only your server-side code does |
What you must do
- Store the key in environment variables only. Never commit it to source control or embed it in client-side code.
- Use the same key value as
INTERNAL_API_KEYon the Hive-Socket server. They must match exactly. - Use a different key per environment — generate a separate key for dev and prod, set them independently.
- Rotate by updating both env vars — update
INTERNAL_API_KEYon Hive-Socket andHIVE_SOCKET_API_KEYon your backends, then redeploy. There is no revocation endpoint; rotation is instant once both sides are updated. - Always pass
projectIdfrom your own config (process.env.SMARTHIVE_PROJECT_ID) — never accept it from user input. It is the SmartHive Auth project ID for your product.
Types
import type {
HiveSocketConfig,
HiveNotifyPayload,
HiveBroadcastPayload,
HiveHealthResponse,
NotificationType,
} from '@smarthivelabs-devs/hive-socket-node';interface HiveSocketConfig {
baseUrl: string;
apiKey: string;
timeout?: number; // ms, default 10000
}
interface HiveNotifyPayload {
projectId: string;
title: string;
body: string;
type: 'info' | 'success' | 'warning' | 'error' | 'message';
metadata?: Record<string, unknown>;
sourceService?: string;
}
interface HiveBroadcastPayload {
projectId: string;
type: string;
payload: Record<string, unknown>;
senderId?: string;
}
interface HiveHealthResponse {
status: 'ok';
connections: number;
uptime: number;
}