Skip to content
All projects

Microservices · CRM · AI

aseller — CRM for Amazon Sellers

A CRM and analytics platform for Amazon sellers and their teams — products, suppliers, orders, shipments, freelance requests, chats and AI tools — built as three Node.js services: REST API, background queue and real-time gateway.

My role
Lead backend developer and architect across all three services — API, queue and real-time gateway.
Stack
  • Node.js
  • Fastify
  • BullMQ
  • Socket.IO
  • PostgreSQL
  • Redis
  • OAuth 2.1
  • MCP

The problem

Seller teams mix many roles — owners, supervisors, researchers, buyers, storekeepers, freelancers — working on the same products, orders and shipments. The platform has to show each of them exactly what they are allowed to see, react in real time, run heavy reports and AI jobs in the background, and let external AI assistants work with the data safely.

Architecture

aseller — CRM for Amazon Sellers — Architecture diagramClientsweb · AI via MCPAPIFastify · OAuthSocket.IORedis adapterPostgreSQLmain DBRedisqueues · pub/subReports DBAmazon parserQueueBullMQ workers
Clients call the Fastify API with short-lived JWTs and keep a WebSocket open to the Socket.IO gateway. The API writes to PostgreSQL and hands long or heavy work — reports, chat fan-out, AI generation, scheduled jobs — to BullMQ workers through Redis. The API and the workers push real-time events through the gateway, which runs on several instances behind the Redis adapter. A separate PostgreSQL holds Amazon reports written by an external parser, and the queue recalculates aggregates from it into the main database. AI assistants connect through MCP with OAuth 2.1.

Key decisions

  1. 01Long AI jobs as asynchronous tasks with long-polling

    Why
    Generation takes minutes while the proxy drops connections after 60 seconds, so already-paid results were being lost. Now the request returns 202, the client long-polls, Redis pub/sub wakes it early and the database row stays the source of truth. Each request carries an idempotency key, and jobs are never retried automatically — a retry means paying twice.
    Trade-off
    More moving parts — job statuses, a sweeper for stuck jobs — and budgets can only be enforced softly.
  2. 02Access control in layers, not in one place

    Why
    URL permissions, roles, ownership scopes in repositories, field-level masking at serialization and notification recipients are each checked where they belong. An MCP key gets the intersection of its own scope and the owner's current permissions, so an AI assistant can never see more than the user.
    Trade-off
    Every new endpoint has to be registered in several layers.
  3. 03Row locks in a fixed order for shipments

    Why
    Two browser tabs recreating the same box used to create duplicates and double the stock. Recreation now runs in a transaction with SELECT … FOR UPDATE in a fixed order and a conditional update; the losing request gets a 409 instead of corrupting data.
    Trade-off
    Every endpoint that touches boxes has to go through the same guard.
  4. 04Read cursors instead of a row per reader

    Why
    Storing who read each message grows with every message times every participant. One cursor per user per chat — and per thread — keeps unread counters simple and the table small.
    Trade-off
    Unread rules live in both the API and the gateway and must stay in sync.

What I built

  • Three services — Fastify REST API, BullMQ workers and a Socket.IO gateway — sharing PostgreSQL and Redis
  • OAuth 2.1 + PKCE provider and MCP server, so Claude and ChatGPT can work with CRM data within the user's permissions
  • Five layers of access control: URL permissions, roles, entity ownership, field-level masking and notification recipients
  • Short-lived access tokens with refresh rotation, multi-session limits, sub-users, admin impersonation and SSO for internal tools
  • Asynchronous AI generation with long-polling, idempotency keys, cancellation and per-user and per-team budgets
  • Chats with threads, reactions, pins and read cursors on a multi-instance Socket.IO with the Redis adapter
  • Separate reports database fed by an external Amazon data parser, with scheduled recalculations in the queue
  • Zero-downtime migrations: concurrent indexes, NOT VALID foreign keys and lock timeouts