Skip to content

medusa-plugin-events

@evolve-framework/medusa-plugin-events · v1.0.0

Forwards Medusa events to Evolve services over HTTP, so a search index, a catalog page or an order email follows what changes in Medusa.

A transport and nothing more. The plugin sends the event name and the id; the receiver decides what that means: mapping onto Evolve's shared message schemas, updating a search index, or ignoring it.

That split is deliberate. It is what lets a consumer see the same events whether commercetools or Medusa is underneath, and it keeps this plugin free of Evolve's framework packages, which would otherwise pull a GraphQL server and its whole toolchain into a Medusa application that never serves GraphQL.

When to use

Install it in every Medusa backend that has Evolve services listening for catalog or order events, such as the catalog subgraph that keeps search current. A Medusa with nothing downstream to tell does not need it.

Get started

  1. Install the package with the peer dependencies it needs. It comes from the Evolve registry, which needs a one-time sign-in.

    pnpm add @evolve-framework/medusa-plugin-events @medusajs/cli @medusajs/framework @medusajs/medusa
    Requirements4
    node>=20
    @medusajs/cli2.21.2
    @medusajs/framework2.21.2
    @medusajs/medusa2.21.2

For example:

// medusa-config.ts
import { defineConfig } from "@medusajs/framework/utils";
module.exports = defineConfig({
// projectConfig, modules and other plugins as before
plugins: [
{
resolve: "@evolve-framework/medusa-plugin-events",
options: {
endpoint: process.env.EVOLVE_EVENTS_URL,
secret: process.env.EVOLVE_EVENTS_SECRET,
},
},
],
});

Options

Option Environment Meaning
endpoint EVOLVE_EVENTS_URL Where to POST every event that no route claims
secret EVOLVE_EVENTS_SECRET Sent as the x-events-secret header
routes A list of { events, endpoint }: send events with these names or prefixes to another endpoint
EVOLVE_EVENTS Comma-separated event names to forward, instead of the default set

Both endpoint and secret fall back to their environment variable, so the options block can be left empty.

No endpoint means no forwarding, silently. A Medusa running on its own with nothing downstream to tell is a valid deployment, not a misconfiguration.

Routing by prefix

A deployment that splits its graph by domain wants each service to hear only its own events. routes sends an event to the first route that names it, or names its prefix: the part before the first dot. So order claims order.placed and order.canceled.

options: {
	routes: [
		{
			events: ["product", "product-category"],
			endpoint: process.env.CATALOG_EVENTS_URL,
		},
		{ events: ["order"], endpoint: process.env.ORDER_EVENTS_URL },
	],
	secret: process.env.EVOLVE_EVENTS_SECRET,
},

An event that no route claims goes to endpoint. The plugin still knows nothing about those domains: it only matches names.

What it sends

POST <endpoint>
content-type: application/json
x-events-secret: <secret>

{ "type": "product.updated", "id": "prod_01...", "timestamp": "2026-08-14T..." }

No payload travels with the event, by design. Medusa's own events carry only an id, and a receiver that needs more can read it back from the right store and locale, which the event could not have known.

The secret is a shared string rather than a signature: the endpoint is meant to be cluster-internal, so it only has to turn away a stray caller, not an adversary who can already read the traffic.

Which events

By default the catalog and the order lifecycle: the catalog so a search index stays in step, and the orders so a shopper is told their order was placed and later shipped.

product.created           product-category.created
product.updated           product-category.updated
product.deleted           product-category.deleted
order.placed              order.fulfillment_created
order.canceled            order.fulfillment_canceled

So installing the plugin and pointing it at the Evolve services is enough to keep search current after an edit in the admin. order.fulfillment_created is Medusa's shipment, since a fulfillment is what actually leaves the warehouse.

Forward a different set with EVOLVE_EVENTS, a comma-separated list that replaces the default:

EVOLVE_EVENTS=order.placed,customer.created

It is an environment variable rather than a plugin option because Medusa reads a subscriber's event list when the module loads, before there is a container to resolve plugin options from.

Failure

A forwarding failure is logged and swallowed. The write really did happen, and throwing here would fail a merchant's save because something downstream was briefly unreachable. A lost event is recoverable with a reindex; a refused write is not. A misconfigured endpoint therefore fails silently: look for Could not forward in Medusa's log.

The plugin does not retry. Make the receiving side idempotent, so that a reindex or an event that arrives twice does no harm.