Quick start
Get Hotpipe running in your Next.js app in five minutes or less. You'll have a working real-time setup with type-safe events, client subscriptions, and server-side publishing.
npm install hotpipePeer dependencies: react (>=18) and zod (>=3).
Hotpipe uses your existing auth — check the session however you normally do. The authorize function runs on every connection attempt. Return null to deny access, or return the user's ID and pipe permissions to grant it. We cover pipe permissions in detail in the pipes & permissions guide.
The catch-all route ([...all]) is required because the handler serves multiple endpoints under your base path — /auth today, with more planned as Hotpipe evolves. This lets the SDK add new endpoints without any changes on your side.
// app/api/realtime/[...all]/route.ts
import { createPipeHandler } from 'hotpipe/server';
export const { POST } = createPipeHandler({
secret: process.env.HOTPIPE_SECRET!,
authorize: async (req) => {
const { session } = await getAuth(req); // your auth
if (!session) return null;
return {
userId: session.userId,
pipes: {}, // we'll configure these in the permissions section
};
},
});This is your single source of truth for event types across client and server. These schemas validate events in both directions — outgoing publishes are checked before sending, and incoming events are verified against the same schema before your handlers run.
// lib/realtime-events.ts
import { z } from 'zod';
export const realtimeEvents = {
'message.created': z.object({
id: z.string(),
text: z.string(),
userId: z.string(),
createdAt: z.number(),
}),
'message.deleted': z.object({
id: z.string(),
}),
'typing.start': z.object({
userId: z.string(),
}),
} as const;// lib/realtime-client.ts
import { createPipeClient } from 'hotpipe/client';
import { realtimeEvents } from './realtime-events';
export const { PipeProvider, usePipe, refreshPipeAuth } = createPipeClient({
events: realtimeEvents,
});This assumes your auth uses cookies (the default for most frameworks like Better Auth, NextAuth, and Clerk). If you're using bearer tokens instead, pass an authHeaders function:
export const { PipeProvider, usePipe, refreshPipeAuth } = createPipeClient({
events: realtimeEvents,
authHeaders: async () => ({
Authorization: `Bearer ${getAccessToken()}`,
}),
});Wrap your app with PipeProvider. This connects everything — your frontend, your backend, and the Hotpipe API.
// app/layout.tsx
import { PipeProvider } from '@/lib/realtime-client';
export default function Layout({ children }: { children: React.ReactNode }) {
return <PipeProvider>{children}</PipeProvider>;
}Publish events from server actions, route handlers, and background jobs. No limits.
// lib/realtime-server.ts
import { createPipePublisher } from 'hotpipe/server';
import { realtimeEvents } from './realtime-events';
export const realtime = createPipePublisher({
secret: process.env.HOTPIPE_SECRET!,
events: realtimeEvents,
});The admin client lets your server revoke pipe access in real time — when a user is kicked from a room, a role changes, or an account is suspended. Revocations take effect immediately, even if the user is currently connected.
// lib/realtime-admin.ts
import { createPipeAdmin } from 'hotpipe/server';
export const pipeAdmin = createPipeAdmin({
secret: process.env.HOTPIPE_SECRET!,
});
