Architecture

How the gateway, backend services, frontend host, and shared libraries fit together at runtime.

erxes is a single Nx/pnpm monorepo. On the backend, each service is an Express app that exposes an Apollo Federation subgraph; the gateway composes them into one supergraph behind a single public /graphql endpoint. On the frontend, one Module Federation host (core-ui) loads a remote per enabled plugin. MongoDB stores tenant data; Redis carries service discovery, session tokens, and every BullMQ queue.

Key concepts

  • One public endpoint. The gateway on port 4000 (main.ts) is the only service clients call. It proxies /graphql to an Apollo Router it spawns on 127.0.0.1:50000, forwards /pl:<name> to a plugin's own HTTP routes (webhooks, callbacks), and falls through to Core API for everything else.
  • Redis service discovery. Every backend registers erxes-service-<name> (its address) and erxesservice:config:<name> (Mongo URL, meta, release version) through service-discovery.ts. The gateway waits for core plus every name in ENABLED_PLUGINS before it composes the supergraph and starts listening.
  • Subdomain tenancy. The gateway derives the tenant from the request hostname; each service builds per-tenant Mongoose models from that subdomain, so the same code serves every organization.
  • Module Federation frontend. core-ui (port 3001) loads <name>_ui/config for each enabled plugin and builds routes, navigation, settings, and widgets from the exported IUIConfig. In production the remote entries come from the plugins.erxes.io CDN, not from your containers (see bootstrap.tsx).
  • Shared libraries with hard boundaries. Backend code uses erxes-api-shared (utils, core-types, core-modules entrypoints); frontend code uses erxes-ui and ui-modules, always through public exports, never another service's source.
  • Background services are not subgraphs. logs (3301) and automations (3302) are BullMQ workers the gateway never routes GraphQL to. Plugins reach them through meta registrations and named <service>-<queue> queues on Redis.

Request flow

Browser ── HTTPS ──> Nginx ── /gateway/ ──> Gateway :4000
                                            │  userMiddleware maps Bearer / app /
                                            │  portal / agent credentials into
                                            │  user, clientportal, cpuser headers
                                            ├─ /graphql ──> Apollo Router 127.0.0.1:50000
                                            │                 ├─> Core API :3300     (core subgraph)
                                            │                 └─> Plugin APIs :3303+ (subgraphs)
                                            ├─ /pl:<name> ──> plugin HTTP routes (webhooks)
                                            └─ / ──> Core API routes
                                                     (/initial-setup, /get-frontend-plugins, ...)

Every service registers in Redis:  erxes-service-<name>,  erxesservice:config:<name>
BullMQ queues on Redis ──> logs-service :3301,  automations-service :3302
core-ui :3001 host loads <name>_ui remotes via Module Federation
(production remote entries served from plugins.erxes.io)

Guides

Was this helpful?