Backend Plugins

Build a plugin API service with tenant isolation, permissions, and gateway registration. This guide follows version 3.1.7 (03acdf712c).

Start with Create a Plugin. For service-to-service calls, see GraphQL & tRPC.

Generated layout

pnpm create-plugin --plugin-name=inventory --module-name=items creates backend/plugins/inventory_api/:

src/
  main.ts                 startPlugin({ name, port })
  connectionResolvers.ts  createGenerateModels per subdomain
  apollo/                 typeDefs + resolvers
  trpc/                   init-trpc.ts, routers, trpcClients
  modules/items/          @types, db/{models,definitions}, graphql/{schemas,resolvers}
  meta/                   permissions, automations, segments, notifications

The generator template uses API port 33010; every generated plugin reuses it, so assign an unused port (Sales uses 3305). The Nx project is named inventory_api.

Runtime contract

backend/erxes-api-shared/src/utils/start-plugin.ts defines startPlugin({ name, port, graphql, apolloServerContext, trpcAppRouter, expressRouter, hasSubscriptions, meta }):

  • Builds tenant models from the request subdomain for GraphQL and tRPC.
  • Serves /health, /graphql (federated subgraph), /trpc, and /agent-tools/* when a tRPC router exists.
  • Keeps connections alive (keepAliveTimeout 120000, headersTimeout 121000).
  • Dispatches meta (automations, segments, documents, notifications, import/export, payments, before/after hooks) and calls joinErxesGateway.

Enable locally with the base name (no _api suffix):

ENABLED_PLUGINS=inventory

Implementation rules

  • Prefix every GraphQL operation with the plugin or module name and keep names unique repo-wide.
  • Check authentication and permissions before sensitive reads and mutations.
  • Define Mongoose schemas explicitly and follow the owning module's new Schema(...) plus schemaWrapper pattern.
  • Keep resolvers thin; put business rules in services or models.
  • Own migrations inside the plugin; never read or mutate another plugin's collections.
  • Maintain the plugin's AGENTS.md bounded recent-changes section when behavior changes.

Source references

Was this helpful?