Updating a 2.x API plugin to work with Reaction API 3.x
The Reaction API plugin system has changed a lot in the API 3.0.0 release. Primarily this is because the API now runs as a NodeJS program instead of a Meteor app, and because the Babel build step has been removed in favor of using Node's built in support for ECMAScript modules. But many other breaking changes were made in 3.0.0 as well. For a summary of changes in 3.0.0, refer to Upgrading and the API Changelog.
If you have custom plugins as part of your Reaction 2.x system, you'll need to go through them one by one and update them according to the following recommendations.
Plugins Should Be NPM Packages
Every plugin should be an NPM package that is an ES module targeting Node 12.
- Must have
"type": "module"
in thepackage.json
- Should have
"engines": { "node": ">=12.14.1" }
in thepackage.json
- Use
import/export
in your files rather thanrequire
, and publish without running the files through Babel. - For a good example, see https://github.com/reactioncommerce/api-utils/blob/trunk/package.json
- The package entry point must
export default
a function that accepts one argument,api
, which is aReactionAPI
instance. In most cases, the only thing this function will do is callapi.registerPlugin
, passing in your plugin options. - If you depend on other Reaction API plugins also being installed, list them as NPM dependencies in your
package.json
if they are published as NPM packages.
By "NPM package" we mean anything that NPM can install. For example, you may want to install your custom plugin packages from private GitHub repos rather than publishing them to the NPM public registry or paying for private NPM. You can even specify a folder within your project as long as it has a
package.json
file.
How to link in a local NPM package for a plugin while working on it
Run docker-compose up -d
before running any of these commands, and wait until the API has fully started.
- Enter
bin/package-link <package-name>
to link locally cloned package repo into the container andnpm link
it.- This assumes that the package lives in
../api-plugins
, which will be true if you're running Reaction via thereaction-development-platform
, it's a plugin package, and you used themake clone-api-plugins
command to clone it. - Alternatively enter
bin/package-link <package-name> <path-to-package-repo>
for any package that is not located in../api-plugins
- This assumes that the package lives in
- When you're done testing, run
bin/package-unlink <package-name>
Some additional notes for linking packages
- It should work to link multiple plugins at once (one at a time)
- If you do not unlink before you stop the
api
service, you will likely get into a loop where the API won't ever start. If this happens, delete the node_modules volume with something likedocker volume rm reaction_api_node_modules
, and thendocker-compose up -d
should work. - When you make changes to the package repo code, run the same
bin/package-link
command again. You do NOT have to (and must not) manually restart theapi
container. The Reaction API will magically restart after the linking finishes.
Plugins Must Import Code According to Node ECMAScript Module Rules
Most named imports do not work at this time and will need to be rewritten. Some examples:
- Use
import accounting from "accounting-js"
and then access functions on theaccounting
object. Named imports don't work. - Use
import _ from "lodash"
. Named imports don't work.
These are not the only examples, and there may be various ways to fix this issue.
- Some packages have ES modules exports, but they're at a different package path. For example, you may be able to add /esm
after the package name. Consult the package docs to see if this is the case.
- If all else fails, you can fall back to using require
, but you need to call createRequire
to get the require
function in an ES module file.
For full details, refer to Understanding Node ECMAScript Modules.
API and UI Plugins are Separate
The Reaction Admin user interface is in a separate project now; it is no longer part of the API codebase. This means that UI components must be loaded and registered differently, in the Reaction Admin project. While you could still technically distribute the components in the same NPM package as the API plugin code, in most cases it's simpler and less confusing to create two different packages.
There are two main functions to register your React component to appear in the Reaction Admin UI: registerOperatorRoute
and registerBlock
registerOperatorRoute
Clone your plugin into /imports/plugins/custom
. Your plugin must contain a single folder named client
in which there is an index.js
file. In this file, import your UI components and register them to appear as routes.
registerBlock
Clone your plugin into /imports/plugins/custom
. Your plugin must contain a single folder named client
in which there is an index.js
file. In this file, import your UI components and register them to appear in a specific block.
Specific UI Component Changes
- If you were previously using
registry
withprovides: ["emailProviderConfig"]
, you now need to register a component for the "EmailSettings" block region. - If you were previously using
registry
withprovides: ["shopSettings"]
, you now need to register a component for the "ShopSettings" block region. - If you were previously using
registry
withprovides: ["paymentSettings"]
, you now need to register a component for the "PaymentSettings" block region. - If you were previously using
registry
withprovides: ["paymentSettings"]
for UI related to discounts, you now need to register a component for the "Discounts" block region. - If you were previously using
registry
withprovides: ["shippingSettings"]
, you now need to register a component for the "ShippingSettings" block region. - If you were previously using
registry
withprovides: ["taxSettings"]
, you now need to register a component for the "TaxSettings" block region.
How to Register API Plugins
In 2.x plugins were detected automatically in the plugins
directory. In 3.x, you must do two manual steps in your project to add an API plugin:
npm install
the plugin package or add it manually to the dependencies list inpackage.json
.- Modify the
registerPlugins.js
file to import and register it, following the pattern in that file.
Manually registering plugins gives you greater control over which plugins load and in which order. The registerPlugins.js
file will sometimes have conflicts to resolve when you pull from upstream, but they won’t be difficult conflicts.
You may create a
/src/custom
folder, add one subfolder per custom plugin, create apackage.json
file for each plugin, and add these as relative path dependencies in the mainpackage.json
. This is a quick way to try out a custom plugin, but we highly recommend that you maintain plugins in individual repos and usenpm install
to install them from a remote URL.
There is No Shop on Context
API functions pass around a context
object that is used to access the database, call other functions, check authentication, and perform authorization. Starting in 3.0.0, context.shop
and context.shopId
are no longer available.
Search your custom plugin code for context.shop
. In these places, you will need to get a shop ID another way. The best ways are to either take it off the related entity you are dealing with, or ask the caller to pass it in (for example, add it to your GraphQL query or mutation params). In a pinch, you can do await context.collections.Shops.findOne({ shopType: "primary" })
, but this will not work as well as Reaction's support for multi-shop configuration grows stronger.