Guide 22

Databases and state for bots: conversation state, sessions, storage choices and data retention

Bots look stateless from outside, but every multi-step flow, setting and reminder needs a place to live that survives restarts.

Guide2 min readUpdated

What to store

  • Identities: platform user and chat IDs (as strings; Telegram IDs can exceed 32-bit integers and Discord snowflakes exceed JavaScript's safe integer range).
  • Preferences: language, time zone (as an IANA name), notification settings.
  • Conversation state: the current step of a flow and the answers collected so far.
  • Jobs: scheduled reminders, pending broadcasts, retries.
  • Idempotency keys: processed update IDs or event IDs, so a redelivered webhook is not handled twice.

Store as little as possible. Not storing message content avoids a whole class of privacy problems.

Choosing storage

Need Good fit
Prototype, single process In-memory map (lost on restart) or SQLite
Small to medium bot on one server SQLite with WAL mode, or PostgreSQL
Several instances, relational data PostgreSQL or MySQL
Sessions, rate limiting, locks, short TTL data Redis or Valkey
Edge or serverless runtimes The platform's key-value store, a serverless SQL service or Durable Objects style coordination

Pick the boring option you know how to back up. Most bots never outgrow one PostgreSQL instance.

Session keys

Decide what a "session" belongs to:

  • Per user for personal settings.
  • Per chat for group features.
  • Per user in chat for multi-step flows in groups, so two people filling a form at once do not overwrite each other.

Frameworks like grammY, python-telegram-bot and Telegraf ship session middleware with pluggable storage adapters.

Modeling flows

Store the flow as a small state machine: {flow: "signup", step: "email", data: {...}, expires_at}. Add an expiry so abandoned flows do not trap users, and always offer /cancel. Design flows visually with the flow designer.

Concurrency

Platforms can deliver several updates for one user at nearly the same time (double taps, retries). Protect read-modify-write sequences:

  • Use database transactions or conditional updates (UPDATE ... WHERE version = ?).
  • Process updates for the same chat sequentially (a per-chat queue or lock).
  • Record processed event IDs with a unique constraint and skip duplicates.

Caching

Cache platform data you read often (chat titles, member roles) with a short TTL and refresh on relevant events. Never treat a cache as the source of truth for permissions on sensitive actions.

Operations

  • Version schema changes with a migration tool and run them in CI/CD (CI/CD for bots).
  • Back up regularly and test restores.
  • Define retention: delete conversation state after it expires, and remove a user's data when they ask or block the bot if your policy says so. See legal, terms and privacy.
  • Encrypt sensitive fields and keep database credentials out of code.

Check your understanding

1. How should Discord snowflake IDs be stored in JavaScript apps?
2. What prevents two group members' forms from colliding?