When developers hear "Zustand," they instinctively think of React hooks and component state. But what if this lightweight state-management library could transcend the frontend to solve backend challenges? One developer’s quest to build an ad-free QR code generator for Telegram uncovered Zustand’s untapped potential—transforming chaotic bot logic into a streamlined, testable system inspired by functional programming principles.

The Genesis: From Annoyance to Architectural Experiment

The project sparked from a collision of frustrations: a deep dive into Zustand’s vanilla createStore API coincided with real-world irritation over clunky QR code websites. "Instead of building another web app," the developer mused, "why not create a Telegram bot?" The platform eliminated UI complexities but introduced state-management nightmares—multi-step conversations, async QR generation, and user-specific settings. Traditional bot frameworks like Telegraf offered convenience but risked entangled logic and untestable code. With just a weekend to deliver a production-ready tool, the solution emerged from an unlikely source: Zustand’s unidirectional data flow.

Taming Bot Chaos with The Elm Architecture

At its core, the bot adopts the Elm Architecture (TEA)—a pattern familiar to Redux users—where actions update state, triggering reactions. Zustand’s vanilla store became the engine:

import { createStore } from 'zustand/vanilla';

export const store = createStore((set) => ({
  chats: [],
  requests: [],
  newRequest: (request) => set(state => {
    state.requests.push({ ...request, state: 'New' });
  }),
  processRequest: (id) => set(state => {
    const req = state.requests.find(r => r.id === id);
    if (req) req.state = 'Processing';
  })
}));

This pure state layer, devoid of React dependencies, enforced strict transitions between states like New → Processing → Completed. Each user message triggered actions (newRequest, processRequest), while a global subscription reacted to changes:

store.subscribe((state) => {
  state.requests.forEach(request => {
    if (request.stateChanged) {
      handleStateChange(request); // Sends Telegram messages/photos
    }
  });
});
Article illustration 1

Reactive state shifts, like tea poured into a cup, drive bot behavior—no manual orchestration needed.

Why Zustand? Testing, Separation, and Scale

Traditional bot code mixes I/O, state, and business logic—creating testing hell. Zustand’s architecture decoupled these concerns:
- Pure State Actions: Business logic lived in store functions, testable without mocks.
- Side-Effect Isolation: Telegram API calls and file I/O occurred in reactive handlers.
- State Machine Rigor: Enums like RequestState prevented invalid transitions.

Unit tests became trivial:

it('transitions request from New to Processing', () => {
  store.getState().newRequest({ id: 1, text: 'test' });
  store.getState().processRequest(1);
  expect(store.getState().requests[0].state).toBe('Processing');
});

This caught race conditions and edge cases early—something nearly impossible with callback-heavy bot frameworks.

Frameworks vs. Flexibility: The Trade-Off

While libraries like Telegraf offer middleware and sessions, they enforce patterns that limit extensibility. By choosing the lower-level node-telegram-bot-api, the developer retained control:
- Multi-Client Future: The same Zustand store could power a CLI, React web app, or desktop UI.
- Observable State: Adding features like analytics required just another subscription.

Article illustration 2

Like Portal turrets snapping to attention, subscriptions react instantly to state shifts.

Yet this approach demands more upfront design—a worthwhile trade-off for systems, not just bots.

Rethinking State Management’s Domain

This experiment proves that patterns like unidirectional flow and reactive state aren’t confined to frontend or functional languages. Zustand’s minimalism brings clarity to any state-heavy context, from bots to IoT backends. For developers drowning in callback chaos, the lesson is clear: sometimes, the best backend tool is a frontend library.

Source: zwit.link/posts/zustand-telegram-bot