One live event
Start with one event that visibly changes the UI or confirms a command.
Realtime sliceRun Socket.IO or gRPC as the primary interface while a separate REST process provides fallback and AsyncAPI documentation.

const socket = io('http://localhost:3001');
socket.emit('request', {
id: crypto.randomUUID(),
subject: 'tasks.create',
payload: { title: 'Realtime task' },
});
socket.on('response', ({ id, payload }) => {
console.log(id, payload);
});From zero to first MVP
Use this path when the first MVP must feel live: progress updates, collaboration, notifications, or command results that return through a bidirectional channel.
Choose the one event users must see without refreshing, such as task.created or task.statusChanged.
MVP output: one AsyncAPI channel and one correlated request/response subject.
type Task = { id: string; title: string; categoryId: string; completed: boolean };
type TaskCommand = {
subject: 'tasks.create';
payload: { title: string; categoryId: string };
};
type TaskEvent =
| { subject: 'tasks.ready'; payload: { total: number } }
| { subject: 'tasks.created'; payload: Task };MVP scope
Start with one event that visibly changes the UI or confirms a command.
Realtime sliceKeep REST available so the first MVP has a supportable recovery path.
Reliability sliceRun realtime and fallback processes explicitly with local or PM2 profiles.
Operations slice| Proof area | Question to answer | Jumentix mechanism | MVP evidence |
|---|---|---|---|
| Correlation | Can the client match every response to its request? | Message id, subject contract, response handler tests. | No anonymous realtime side effects. |
| Fallback | Does REST return the same result when realtime is unavailable? | Fallback route and shared use-case contract. | Degraded mode remains usable. |
| Operations | Can the process be started, inspected and restarted? | PM2/runtime profile and logs. | The MVP is demoable outside a dev terminal. |
Practical implementation
These examples keep Category and Task as the product vocabulary and show the controller, contract, client, worker, or state layer needed to reach a runnable MVP.
type Task = { id: string; title: string; categoryId: string; completed: boolean };
type Client = { send: (message: string) => void };
const tasks = new Map<string, Task>();
const clients = new Set<Client>();
function broadcast(subject: string, payload: unknown) {
const message = JSON.stringify({ subject, payload });
for (const client of clients) client.send(message);
}
export function connectTaskSocket(client: Client) {
clients.add(client);
client.send(JSON.stringify({ subject: 'tasks.ready', payload: { total: tasks.size } }));
return () => clients.delete(client);
}
export async function handleTaskMessage(message: { subject: string; payload: { title: string; categoryId: string } }) {
if (message.subject !== 'tasks.create') return { ok: false, error: 'unsupported subject' };
const task: Task = {
id: crypto.randomUUID(),
title: message.payload.title,
categoryId: message.payload.categoryId,
completed: false
};
tasks.set(task.id, task);
broadcast('tasks.created', task);
return { ok: true, result: task };
}Keep exploring
Ship OpenAPI 3.1 services with interchangeable native HTTP adapters.
Explore the blueprintRun Socket.IO or gRPC beside a REST fallback and AsyncAPI documentation.
Explore the blueprintLaunch one deployable with domain boundaries ready to become services.
Explore the blueprintKeep service communication contract-based with the Message Mediator.
Explore the blueprintBuild frontend products that share generated SDKs and work offline.
Explore the blueprintExplore the source, run the factory locally, and turn your next Node.js service into a repeatable platform capability.