www/apps/book/app/learn/fundamentals/plugins/create/page.mdx
import { Prerequisites, CardList, Table } from "docs-ui"
export const metadata = {
title: ${pageNumber} Create a Plugin,
}
In this chapter, you'll learn how to create a Medusa plugin and publish it.
A plugin is a package of reusable Medusa customizations that you can install in any Medusa application. By creating and publishing a plugin, you can reuse your Medusa customizations across multiple projects or share them with the community.
<Note>Plugins are available starting from Medusa v2.3.0.
</Note>Plugins are created in a separate Medusa project. This makes the development and publishing of the plugin easier. Later, you'll install that plugin in your Medusa application to test it out and use it.
Medusa's create-medusa-app CLI tool provides the option to create a plugin project. Run the following command to create a new plugin project:
npx create-medusa-app my-plugin --plugin
This will create a new Medusa plugin project in the my-plugin directory.
After the installation is done, the plugin structure will look like this:
src/: Contains the Medusa customizations.src/admin: Contains admin extensions.src/api: Contains API routes and middlewares. You can add store, admin, or any custom API routes.src/jobs: Contains scheduled jobs.src/links: Contains module links.src/modules: Contains modules.src/provider: Contains module providers.src/subscribers: Contains subscribers.src/workflows: Contains workflows. You can also add hooks under src/workflows/hooks.package.json: Contains the plugin's package information, including general information and dependencies.tsconfig.json: Contains the TypeScript configuration for the plugin.Before developing, testing, and publishing your plugin, make sure its name in package.json is correct. This is the name you'll use to install the plugin in your Medusa application.
For example:
{
"name": "@myorg/plugin-name",
// ...
}
Medusa scrapes NPM for a list of plugins that integrate third-party services, to later showcase them on the Medusa website. If you want your plugin to appear in that listing, make sure to add the medusa-v2 and medusa-plugin-integration keywords to the keywords field in package.json.
Only plugins that integrate third-party services are listed in the Medusa integrations page. If your plugin doesn't integrate a third-party service, you can skip this step.
</Note>{
"keywords": [
"medusa-plugin-integration",
"medusa-v2"
],
// ...
}
In addition, make sure to use one of the following keywords based on your integration type:
<Table> <Table.Header> <Table.Row> <Table.HeaderCell> Keyword </Table.HeaderCell> <Table.HeaderCell> Description </Table.HeaderCell> <Table.HeaderCell> Example </Table.HeaderCell> </Table.Row> </Table.Header> <Table.Body> <Table.Row> <Table.Cell> `medusa-plugin-analytics` </Table.Cell> <Table.Cell> Analytics service integration </Table.Cell> <Table.Cell> Google Analytics </Table.Cell> </Table.Row> <Table.Row> <Table.Cell> `medusa-plugin-auth` </Table.Cell> <Table.Cell> Authentication service integration </Table.Cell> <Table.Cell> Auth0 </Table.Cell> </Table.Row> <Table.Row> <Table.Cell> `medusa-plugin-cms` </Table.Cell> <Table.Cell> CMS service integration </Table.Cell> <Table.Cell> Contentful </Table.Cell> </Table.Row> <Table.Row> <Table.Cell> `medusa-plugin-notification` </Table.Cell> <Table.Cell> Notification service integration </Table.Cell> <Table.Cell> Twilio SMS </Table.Cell> </Table.Row> <Table.Row> <Table.Cell> `medusa-plugin-payment` </Table.Cell> <Table.Cell> Payment service integration </Table.Cell> <Table.Cell> PayPal </Table.Cell> </Table.Row> <Table.Row> <Table.Cell> `medusa-plugin-search` </Table.Cell> <Table.Cell> Search service integration </Table.Cell> <Table.Cell> MeiliSearch </Table.Cell> </Table.Row> <Table.Row> <Table.Cell> `medusa-plugin-shipping` </Table.Cell> <Table.Cell> Shipping service integration </Table.Cell> <Table.Cell> DHL </Table.Cell> </Table.Row> <Table.Row> <Table.Cell> `medusa-plugin-other` </Table.Cell> <Table.Cell> Other third-party integrations </Table.Cell> <Table.Cell> Sentry </Table.Cell> </Table.Row> </Table.Body> </Table>Your plugin project will already have the dependencies mentioned in this section. Unless you made changes to the dependencies, you can skip this section.
</Note>In the package.json file you must have the Medusa dependencies as devDependencies and peerDependencies. In addition, you must have @swc/core as a devDependency, as it's used by the plugin CLI tools.
For example, assuming 2.5.0 is the latest Medusa version:
{
"devDependencies": {
"@medusajs/admin-sdk": "2.5.0",
"@medusajs/cli": "2.5.0",
"@medusajs/framework": "2.5.0",
"@medusajs/medusa": "2.5.0",
"@medusajs/test-utils": "2.5.0",
"@medusajs/ui": "4.0.4",
"@medusajs/icons": "2.5.0",
"@swc/core": "1.5.7",
},
"peerDependencies": {
"@medusajs/admin-sdk": "2.5.0",
"@medusajs/cli": "2.5.0",
"@medusajs/framework": "2.5.0",
"@medusajs/test-utils": "2.5.0",
"@medusajs/medusa": "2.5.0",
"@medusajs/ui": "4.0.3",
"@medusajs/icons": "2.5.0",
}
}
Your plugin project will already have the exports mentioned in this section. Unless you made changes to the exports or you created your plugin before Medusa v2.7.0, you can skip this section.
</Note>In the package.json file, make sure your plugin has the following exports:
{
"exports": {
"./package.json": "./package.json",
"./workflows": "./.medusa/server/src/workflows/index.js",
"./.medusa/server/src/modules/*": "./.medusa/server/src/modules/*/index.js",
"./providers/*": "./.medusa/server/src/providers/*/index.js",
"./admin": {
"import": "./.medusa/server/src/admin/index.mjs",
"require": "./.medusa/server/src/admin/index.js",
"default": "./.medusa/server/src/admin/index.js"
},
"./*": "./.medusa/server/src/*.js"
}
}
Aside from the ./package.json, ./providers, and ./admin, these exports are only a recommendation. You can cherry-pick the files and directories you want to export.
The plugin exports the following files and directories:
./package.json: The package.json file. Medusa needs to access the package.json when registering the plugin../workflows: The workflows exported in ./src/workflows/index.ts../.medusa/server/src/modules/*: The definition file of modules. This is useful if you create links to the plugin's modules in the Medusa application../providers/*: The definition file of module providers. This is useful if your plugin includes a module provider, allowing you to register the plugin's providers in Medusa applications. Learn more in the Create Module Providers section../admin: The admin extensions exported in ./src/admin/index.ts../*: Any other files in the plugin's src directory.Medusa's CLI tool provides commands to simplify developing and testing your plugin in a local Medusa application. You start by publishing your plugin in the local package registry, then install it in your Medusa application. You can then watch for changes in the plugin as you develop it.
<Prerequisites items={[ { text: "Medusa application installed.", link: "/learn/installation", } ]} />
The first time you create your plugin, you need to publish the package into a local package registry, then install it in your Medusa application. This is a one-time only process.
To publish the plugin to the local registry, run the following command in your plugin project:
npx medusa plugin:publish
This command uses Yalc under the hood to publish the plugin to a local package registry. The plugin is published locally under the name you specified in package.json.
Next, navigate to your Medusa application:
cd ~/path/to/medusa-app
Make sure to replace ~/path/to/medusa-app with the path to your Medusa application.
Then, if your project was created before v2.3.1 of Medusa, make sure to install yalc as a development dependency:
npm install --save-dev yalc
After that, run the following Medusa CLI command to install the plugin:
npx medusa plugin:add @myorg/plugin-name
Make sure to replace @myorg/plugin-name with the name of your plugin as specified in package.json. Your plugin will be installed from the local package registry into your Medusa application.
After installing the plugin, you need to register it in your Medusa application in the configurations defined in medusa-config.ts.
Add the plugin to the plugins array in the medusa-config.ts file:
export const pluginHighlights = [
["5", "@myorg/plugin-name", "Replace with your plugin name."],
]
module.exports = defineConfig({
// ...
plugins: [
{
resolve: "@myorg/plugin-name",
options: {},
},
],
})
The plugins configuration is an array of objects where each object has a resolve key whose value is the name of the plugin package.
Each plugin configuration also accepts an options property, whose value is an object of options to pass to the plugin's modules.
For example:
export const pluginOptionsHighlight = [ ["6", "options", "Options to pass to the plugin's modules."] ]
module.exports = defineConfig({
// ...
plugins: [
{
resolve: "@myorg/plugin-name",
options: {
apiKey: true,
},
},
],
})
The options property in the plugin configuration is passed to all modules in the plugin. Learn more about module options in this chapter.
While developing your plugin, you can watch for changes in the plugin and automatically update the plugin in the Medusa application using it. This is the only command you'll continuously need during your plugin development.
To do that, run the following command in your plugin project:
npx medusa plugin:develop
This command will:
You can start your Medusa application's development server to test out your plugin:
npm run dev
While your Medusa application is running and the plugin is being watched, you can test your plugin while developing it in the Medusa application.
You can now build your plugin's customizations. The following guide explains how to build different customizations in your plugin.
<CardList items={[ { title: "Create a module", href: "/learn/fundamentals/modules", }, { title: "Create a module link", href: "/learn/fundamentals/module-links", }, { title: "Create a workflow", href: "/learn/fundamentals/workflows", }, { title: "Add a workflow hook", href: "/learn/fundamentals/workflows/add-workflow-hook", }, { title: "Create an API route", href: "/learn/fundamentals/api-routes", }, { title: "Add a subscriber", href: "/learn/fundamentals/events-and-subscribers", }, { title: "Add a scheduled job", href: "/learn/fundamentals/scheduled-jobs", }, { title: "Add an admin widget", href: "/learn/fundamentals/admin/widgets", }, { title: "Add an admin UI route", href: "/learn/fundamentals/admin/ui-routes", } ]} className="mb-1.5" />
While building those customizations, you can test them in your Medusa application by watching the plugin changes and starting the Medusa application.
During your development, you may need to generate migrations for modules in your plugin. To do that, first, add the following environment variables in your plugin project:
DB_USERNAME=postgres
DB_PASSWORD=123...
DB_HOST=localhost
DB_PORT=5432
DB_NAME=db_name
You can add these environment variables in a .env file in your plugin project. The variables are:
DB_USERNAME: The username of the PostgreSQL user to connect to the database.DB_PASSWORD: The password of the PostgreSQL user to connect to the database.DB_HOST: The host of the PostgreSQL database. Typically, it's localhost if you're running the database locally.DB_PORT: The port of the PostgreSQL database. Typically, it's 5432 if you're running the database locally.DB_NAME: The name of the PostgreSQL database to connect to.Then, run the following command in your plugin project to generate migrations for the modules in your plugin:
npx medusa plugin:db:generate
This command generates migrations for all modules in the plugin.
Finally, run these migrations on the Medusa application that the plugin is installed in using the db:migrate command:
npx medusa db:migrate
The migrations in your application, including your plugin, will run and update the database.
In the Prepare Plugin section, you learned about exported resources in your plugin.
These exports allow you to import your plugin resources in your Medusa application, including workflows, links and modules.
For example, to import the plugin's workflow in your Medusa application:
<Note title="Tip">@myorg/plugin-name is the plugin package's name.
import { Workflow1, Workflow2 } from "@myorg/plugin-name/workflows"
import BlogModule from "@myorg/plugin-name/modules/blog"
// import other files created in plugin like ./src/types/blog.ts
import BlogType from "@myorg/plugin-name/types/blog"
The exported resources also allow you to import module providers in your plugin and register them in the Medusa application's configuration. A module provider is a module that provides the underlying logic or integration related to a commerce or Infrastructure Module.
For example, assuming your plugin has a Notification Module Provider called my-notification, you can register it in your Medusa application's configuration like this:
@myorg/plugin-name is the plugin package's name.
module.exports = defineConfig({
// ...
modules: [
{
resolve: "@medusajs/medusa/notification",
options: {
providers: [
{
resolve: "@myorg/plugin-name/providers/my-notification",
id: "my-notification",
options: {
channels: ["email"],
// provider options...
},
},
],
},
},
],
})
You pass to resolve the path to the provider relative to the plugin package. So, in this example, the my-notification provider is located in ./src/providers/my-notification/index.ts of the plugin.
To learn how to create module providers, refer to the following guides:
<CardList items={[ { title: "File Module Provider", href: "!resources!/references/file-provider-module", }, { title: "Notification Module Provider", href: "!resources!/references/notification-provider-module", }, { title: "Auth Module Provider", href: "!resources!/references/auth/provider", }, { title: "Payment Module Provider", href: "!resources!/references/payment/provider", }, { title: "Fulfillment Module Provider", href: "!resources!/references/fulfillment/provider", }, { title: "Tax Module Provider", href: "!resources!/references/tax/provider", }, ]} className="mb-1.5" />
Make sure to add the keywords mentioned in the Package Keywords section in your plugin's package.json file.
Medusa's CLI tool provides a command that bundles your plugin to be published to npm. Once you're ready to publish your plugin publicly, run the following command in your plugin project:
npx medusa plugin:build
The command will compile an output in the .medusa/server directory.
You can now publish the plugin to npm using the NPM CLI tool. Run the following command to publish the plugin to npm:
npm publish
If you haven't logged in before with your NPM account, you'll be asked to log in first. Then, your package is published publicly to be used in any Medusa application.
You install a plugin that's published publicly using your package manager. For example:
npm install @myorg/plugin-name
Where @myorg/plugin-name is the name of your plugin as published on NPM.
Then, register the plugin in your Medusa application's configurations as explained in this section.
To update the Medusa dependencies in a plugin, refer to this documentation.
</Note>If you've published a plugin and you've made changes to it, you'll have to publish the update to NPM again.
First, run the following command to change the version of the plugin:
npm version <type>
Where <type> indicates the type of version update you’re publishing. For example, it can be major or minor. Refer to the npm version documentation for more information.
Then, re-run the same commands for publishing a plugin:
npx medusa plugin:build
npm publish
This will publish an updated version of your plugin under a new version.