--- description: 'Learn how to create a plugin in Medusa. This guide explains how to develop, configure, and test a plugin.' addHowToData: true --- import DocCardList from '@theme/DocCardList'; import Icons from '@theme/Icon'; import LearningPath from '@site/src/components/LearningPath'; import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; # How to Create a Plugin In this document, you’ll learn how to create a plugin and some tips for development. If you’re interested to learn more about what plugins are and where to find available official and community plugins, check out the [overview document](./overview.mdx). Alternatively, you can follow this recipe to create a plugin with step-by-step guidance. ## Prerequisites You must have an existing Medusa project that you want to create the plugin with. The recommended way to create a plugin is using the `new` command from Medusa CLI: ```bash npx @medusajs/medusa-cli@latest new medusa-plugin-custom ``` Where `medusa-plugin-custom` is the name of the plugin you’re creating. In Medusa, plugins are named based on their functionalities. By convention, all plugin names start with `medusa` followed by a descriptive name of what the plugin does. For example, the Stripe plugin is named `medusa-payment-stripe`. --- ## Changes to package.json ### Package Name By default, your package name in `package.json` will be `medusa-starter-default`. This should instead be the name of your plugin. For example, the Stripe plugin's package name is `medusa-payment-stripe`. ### Change Dependencies A basic Medusa backend installed with the `medusa new` command has dependencies that are necessary for the backend, but not necessary for plugins. For example, can remove the dependencies `medusa-fulfillment-manual`, `medusa-payment-manual`, and `medusa-payment-stripe` as they are fulfillment and payment plugins necessary for a Medusa backend, but not for a plugin. The same goes for modules like `@medusajs/cache-inmemory`. Additionally, you can remove `@medusajs/medusa-cli` as you don’t need to use the Medusa CLI while developing a plugin. You should also add `@medusajs/medusa` as a peer dependency: ```json "peerDependencies": { "@medusajs/medusa": "YOUR_MEDUSA_VERSION", // other peer dependencies... } ``` Where `YOUR_MEDUSA_VERSION` is the version you're using of the Medusa core package. You should be able to find it under `devDependencies`. Once you’re done making these changes, re-run the install command to update your `node_modules` directory: ```bash npm2yarn npm install ``` Then, make sure to remove the plugins and modules you removed from `medusa-config.js`: ```js title="medusa-config.js" // previously had plugins const plugins = [] // previously had modules const modules = {} ``` ### Changes for Admin Plugins If your plugin contains customizations to the admin dashboard, it's recommended to create different `tsconfig` files for backend and admin customizations, then modify the scripts in `package.json` to handle building backend and admin customizations separately. :::note These changes may already be available in your Medusa project. They're included here for reference purposes. ::: Start by updating your `tsconfig.json` with the following configurations: ```json title="tsconfig.json" { "compilerOptions": { "target": "es2019", "module": "commonjs", "allowJs": true, "checkJs": false, "jsx": "react-jsx", "declaration": true, "outDir": "./dist", "rootDir": "./src", "experimentalDecorators": true, "emitDecoratorMetadata": true, "noEmit": false, "strict": false, "moduleResolution": "node", "esModuleInterop": true, "resolveJsonModule": true, "skipLibCheck": true, "forceConsistentCasingInFileNames": true }, "include": ["src/"], "exclude": [ "dist", "build", ".cache", "tests", "**/*.spec.js", "**/*.spec.ts", "node_modules", ".eslintrc.js" ] } ``` The important changes to note here are the inclusion of the field `"jsx": "react-jsx"` and the addition of `"build"` and `“.cache”` to `exclude`. The addition of `"jsx": "react-jsx"` specified how should TypeScript transform JSX, and excluding `build` and `.cache` ensures that TypeScript ignores build and development files. Next, create the file `tsconfig.server.json` with the following content: ```json title="tsconfig.server.json" { "extends": "./tsconfig.json", "compilerOptions": { /* Emit a single file with source maps instead of having a separate file. */ "inlineSourceMap": true }, "exclude": ["src/admin", "**/*.spec.js"] } ``` This is the configuration that will be used to transpile your custom backend code, such as services or entities. The important part is that it excludes `src/admin` as that is where your Admin code will live. Then, create the file `tsconfig.admin.json` with the following content: ```json title="tsconfig.admin.json" { "extends": "./tsconfig.json", "compilerOptions": { "module": "esnext" }, "include": ["src/admin"], "exclude": ["**/*.spec.js"] } ``` This is the configuration that will be used when transpiling your admin code. Finally, update the `build` scripts in your project and add a new `prepare` command: ```json title="package.json" "scripts": { // other scripts... "build": "cross-env npm run clean && npm run build:server && npm run build:admin", "build:server": "cross-env npm run clean && tsc -p tsconfig.json", "build:admin": "cross-env medusa-admin build", "prepare": "cross-env NODE_ENV=production npm run build:server && medusa-admin bundle" } ``` Each of these scripts do the following: - `build`: used to build resources for both admin and backend for development. You'll typically use this script during your plugin development. - `build:server`: used to build backend resources for development. - `build:admin`: used to build admin resources for development. - `prepare`: used to build resources for publishing. You'll typically use this script during plugin testing and publishing. Furthermore, make sure to add `react` to `peerDependencies` along with `react-router-dom` if you're using it: ```json title="package.json" "peerDependencies": { // other dependencies... "react": "^18.2.0", "react-router-dom": "^6.13.0" } ``` ### Delete Irrelevant Files If you've installed the Medusa backend using the [create-medusa-app](../../create-medusa-app.mdx) command, you might find files under the `src` sub-directories that aren't necessary for your plugin development. For example, `src/model/onboarding.ts` or migrations under the `src/migrations` directory. Make sure to delete these files if you're not using them in your plugin. --- ## Plugin Development ### Plugin Structure While developing your plugin, you can create your TypeScript or JavaScript files under the `src` directory. This includes creating services, API Routes, migrations, and other resources. However, before you test the changes on a Medusa backend or publish your plugin, you must transpile your files and move them either to a `dist` directory or to the root of the plugin's directory. For example, if you have an API Route in `src/api/store/custom/route.ts`, after running the `build` or `watch` commands [as defined earlier](#recommended-change-scripts), the file should be transpiled into `dist/api/store/custom/route.ts` in your plugin's root. You can alternative transpile them into the `api/store/custom/route.ts` in your plugin's root. ### Development Resources This guide doesn't cover how to create different files and components. If you’re interested in learning how to do that, you can check out these guides: If you're developing something specific, such as a payment processor plugin, you can follow one of the following guides to learn how to create different services within your plugin. ### Plugin Options Plugins often allow developers that will later use them to provide their own option. For example, you can allow developers to specify the API key of a service you’re integrating. Developers that use your plugin will pass options to your plugin in the `plugins` array in `medusa-config.js`: ```js title="medusa-config.js" const plugins = [ // ... { resolve: `medusa-plugin-custom`, options: { name: "My Store", }, }, ] ``` In your plugin's services, you can have access to the option in their constructor. The options are passed as a second parameter to the `constructor` method. For example: ```js title="src/service/my.ts" // In a service in your plugin class MyService extends TransactionBaseService { constructor(container, options) { super(container) // options contains plugin options this.name = options.name } // ... } ``` :::tip Make sure to include in the README of your plugin the options that can be passed to a plugin. ::: ### enableUI Plugin Option All plugins accept an option named `enableUI`. This option is useful mainly if your plugin contains admin customizations. It allows users to enable or disable admin customizations in the admin dashboard. A developer using your plugin can pass the `enableUI` option as part of the plugin's options: ```js title="medusa-config.js" const plugins = [ // ... { resolve: `medusa-plugin-custom`, options: { // other options enableUI: true, }, }, ] ``` If you're passing your plugin options to third-party services, make sure to omit it from the plugin options you receive in your resources, such as services. The `enableUI` option will always be passed as part of your plugin options. For example: ```js title="src/service/test.ts" // In a service in your plugin class MyService extends TransactionBaseService { constructor(container, options) { super(container) // options contains plugin options const { enableUI, ...otherOptions } = options // pass otherOptions to a third-party service const client = new Client(otherOptions) } // ... } ``` :::note `enableUI`'s default value is `false` if not provided by the plugin users. This means that it must be enabled manually in a plugin's option for the customizations to appear in the admin dashboard. ::: --- ## Test Your Plugin While you develop your plugin, you’ll need to test it on an actual Medusa backend. This can be done using the [npm link](https://docs.npmjs.com/cli/v8/commands/npm-link) command. ### Step 1: Build Changes In the root of your plugin directory, run the `build` command: ```bash npm run build ``` In the root of your plugin directory, run the `prepare` command: ```bash npm run prepare ``` If the `prepare` script is not available in your project, you can find it in [this section](#changes-for-admin-plugins). ### Step 2: Link Package In the root of your plugin directory, run the following command: ```bash npm2yarn npm link ``` Then, in the directory of the Medusa backend you want to test the plugin on, run the following command: ```bash npm2yarn npm link medusa-plugin-custom ``` Where `medusa-plugin-custom` is the package name of your plugin. ### Step 3: Remove Medusa Dependency As your plugin has the `@medusajs/medusa` package installed, and the Medusa backend has `@medusajs/medusa` installed as well, this can cause dependency errors. To avoid that, remove the `@medusajs` directory from the `node_modules` of your plugin's directory. For Unix-based operating systems you can use the following command: ```bash rm -rf node_modules/@medusajs ``` ### Step 4: Add Plugin to Configurations In the `medusa-config.js` file of the Medusa backend you're testing the plugin on, add your custom plugin to the `plugins` array: ```js const plugins = [ // other plugins... { resolve: `medusa-plugin-custom`, options: { // plugin options... // if plugin has admin customizations: enableUI: true, }, }, ] ``` Make sure to change `medusa-plugin-custom` with the name of your plugin. Also, if your plugin has admin customizations, make sure to include the [enableUI](#enableui-plugin-option) option. ### (Optional) Step 5: Run Migrations If your plugin includes migrations, run the following command in the Medusa backend's directory: ```bash npx medusa migrations run ``` ### Step 6: Run the Medusa Backend In the directory of the Medusa backend, start the backend with the `dev` command passing it the `--preserve-symlinks` option: ```bash npm run dev -- -- --preserve-symlinks ``` ```bash yarn dev -- -- --preserve-symlinks ``` ```bash yarn dev -- --preserve-symlinks ``` ```bash pnpm run dev -- -- --preserve-symlinks ``` ### Making Changes to the Plugin While testing your plugin, if you need to make changes you need to re-install the plugin's dependencies: ```bash npm2yarn npm install ``` Then, after making the changes, run the steps [one](#step-1-build-changes), [three](#step-3-remove-medusa-dependency), and [six](#step-6-run-the-medusa-backend) mentioned above. ### Troubleshoot Errors #### Error: The class must be a valid service implementation Please make sure that your plugin is following the correct structure. If the error persists then please try the following fix: ```bash npm2yarn cd /node_modules/medusa-interfaces npm link cd /node_modules/@medusajs/medusa npm link cd rm -rf node_modules/medusa-interfaces rm -rf node_modules/@medusajs/medusa npm link medusa-interfaces npm link @medusajs/medusa npm link cd npm link your-plugin ``` Where `` is the path to your Medusa backend and `` is the path to your plugin. This links the `medusa-interfaces` and `@medusajs/medusa` packages from your `medusa-backend` to your plugin directory and then links your plugin to your `medusa-backend`. #### APIs not loading If the APIs you added to your Medussa backend are not loading then please try the following steps: ```bash npm2yarn cd rm -rf node_modules cd /node_modules/ npm install cd npm run build cd npm run start ``` Where `` is the path to your Medusa backend, `` is the path to your plugin and `` is the name of your plugin as it is in your plugin `package.json` file. --- ## Publish Plugin Once you're done with the development of the plugin, you can publish it to NPM so that other Medusa developers and users can use it. Please refer to [this guide on required steps to publish a plugin](./publish.mdx).