* docs: added manage reservations user guide (#4290) * docs: added manage reservations user guide * removed feature flag details * docs: added how-to for custom reservations (#4292) * docs: added how-to for custom reservations * eslint fixes * docs: added product module documentation (#4287) * docs: added product module documentation * added details about optional environment variables * small fixes * Remove reference link * added example usages * added link to sample project * address PR feedback * docs: moved product module guide + added product module tabs (#4307) * added product module tab * adjust design of badge * docs: added onboarding features (#4168) * added marketplace page * added subscription roadmap * added rating for onboarding * added learning path components * small fixes * fix build error * fix eslint errors * change roadmaps to recipes * small change in text * optimize learning path and notifications * fix tracking usage * fix eslint errors * added enter/exit animation * allow starting a path using a query parameter * fix gap between notifications * address vercel comments * fixed links issue * changed create-medusa-app docs steps * move troubleshooting section * improved tracking across docs * fix build errors * remove console * added a note about `boilerplate` option * added troubleshooting section for eagain * added invite option in cli reference * added track event for finished onboarding * update boilerplate option name * redesigned learning path component * docs: added how to create widget docs (#4318) * docs: added how to create widget docs * remove development guide * added types * docs: added details about createCustomAdminHooks (#4288) * docs: added details about createCustomAdminHooks * small improvement * added missing import * small changes * docs: added onboarding guide (#4320) * docs: added how to create widget docs * remove development guide * docs: added onboarding guide * added types * added recipes link * small adjustments * fixed eslint errors * styling fixes * change to singular product module * updated the what's new section * shorten down medusa react card * updated tailwind configurations * fix build error * fix newspaper icon * style fixes * change modal shadow * fix color of line numbers * fix code fade color * docs: updated admin documentations * eslint fixes * text changes * added a note about beta version * remove empty object argument * remove demo repo url * fix selection color for code headers * general fixes * fix eslint error * changed code theme * added preparation step * changes regarding beta version * Update docs/content/modules/products/serverless-module.md Co-authored-by: Riqwan Thamir <rmthamir@gmail.com> * Update docs/content/modules/products/serverless-module.md Co-authored-by: Riqwan Thamir <rmthamir@gmail.com> --------- Co-authored-by: Riqwan Thamir <rmthamir@gmail.com> Co-authored-by: Oliver Windall Juhl <59018053+olivermrbl@users.noreply.github.com>
598 lines
18 KiB
Plaintext
598 lines
18 KiB
Plaintext
---
|
||
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';
|
||
|
||
# How to Create a Plugin
|
||
|
||
In this document, you’ll learn how to create a plugin and some tips for develoment. 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).
|
||
|
||
## Prerequisites
|
||
|
||
This guide uses the Medusa CLI throughout different steps. If you don’t have the Medusa CLI installed you can install it with the following command:
|
||
|
||
```bash npm2yarn
|
||
npm install @medusajs/medusa-cli -g
|
||
```
|
||
|
||
:::note
|
||
|
||
If you run into any errors while installing the CLI tool, check out the [troubleshooting guide](../../troubleshooting/cli-installation-errors.mdx).
|
||
|
||
:::
|
||
|
||
---
|
||
|
||
## Initialize Project
|
||
|
||
The recommended way to create a plugin is using the Medusa CLI. Run the following command to create a new Medusa project:
|
||
|
||
```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
|
||
|
||
### Change Dependencies
|
||
|
||
A basic Medusa backend installed with the `medusa new` command has dependencies similar to this:
|
||
|
||
```json title=package.json
|
||
"dependencies": {
|
||
"@babel/preset-typescript": "^7.21.4",
|
||
"@medusajs/cache-inmemory": "^1.8.0",
|
||
"@medusajs/cache-redis": "^1.8.0",
|
||
"@medusajs/event-bus-local": "^1.8.0",
|
||
"@medusajs/event-bus-redis": "^1.8.0",
|
||
"@medusajs/medusa": "^1.8.0",
|
||
"@medusajs/medusa-cli": "^1.3.9",
|
||
"babel-preset-medusa-package": "^1.1.13",
|
||
"body-parser": "^1.19.0",
|
||
"cors": "^2.8.5",
|
||
"express": "^4.17.2",
|
||
"medusa-fulfillment-manual": "^1.1.37",
|
||
"medusa-interfaces": "^1.3.7",
|
||
"medusa-payment-manual": "^1.0.23",
|
||
"medusa-payment-stripe": "^2.0.0",
|
||
"typeorm": "^0.3.11"
|
||
},
|
||
"devDependencies": {
|
||
"@babel/cli": "^7.14.3",
|
||
"@babel/core": "^7.14.3",
|
||
"@types/express": "^4.17.13",
|
||
"@types/jest": "^27.4.0",
|
||
"@types/node": "^17.0.8",
|
||
"babel-preset-medusa-package": "^1.1.13",
|
||
"cross-env": "^5.2.1",
|
||
"eslint": "^6.8.0",
|
||
"jest": "^27.3.1",
|
||
"mongoose": "^5.13.14",
|
||
"rimraf": "^3.0.2",
|
||
"ts-jest": "^27.0.7",
|
||
"ts-loader": "^9.2.6",
|
||
"typescript": "^4.5.2"
|
||
},
|
||
```
|
||
|
||
For a plugin, some dependencies are not necessary. For example, can remove the packages `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.
|
||
|
||
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 = {}
|
||
```
|
||
|
||
### Recommended: Change Scripts
|
||
|
||
It's recommended to remove the `seed` and `start` scripts from your `package.json` as they aren't necessary for plugin development.
|
||
|
||
Furthermore, if you don't have a `watch` command in your `package.json` it's recommended to add it:
|
||
|
||
```json title=package.json
|
||
"scripts": {
|
||
// other scripts...
|
||
"watch": "tsc --watch"
|
||
}
|
||
```
|
||
|
||
The `watch` command makes the [testing of the plugin](#test-your-plugin) easier.
|
||
|
||
:::tip
|
||
|
||
The `watch` command outputs the files in the destination specified in the value of `outDir` in `tsconfig.json`, and the same goes for the `build` command. If you made changes to `tsconfig.json`, make sure the destination is either the `dist` directory or the root of the plugin. You can learn more in the [plugin structure section](#plugin-structure).
|
||
|
||
:::
|
||
|
||
### Changes for Admin Plugins
|
||
|
||
:::note
|
||
|
||
Admin customizations are currently in beta and require you to use the `beta` version of `@medusajs/admin` and `@medusajs/medusa`. You can install it with the following command:
|
||
|
||
```bash npm2yarn
|
||
npm install @medusajs/admin@beta @medusajs/medusa@beta
|
||
```
|
||
|
||
:::
|
||
|
||
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` script in your project:
|
||
|
||
```json title=package.json
|
||
"scripts": {
|
||
// other scripts...
|
||
"build": "tsc -p ./tsconfig.server.json && medusa-admin bundle"
|
||
}
|
||
```
|
||
|
||
This `build` script builds the backend customizations, then bundles the admin plugin using `medusa-admin bundle`.
|
||
|
||
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"
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## Develop your Plugin
|
||
|
||
Now, You can start developing your plugin. This can include adding services, endpoints, entities, admin customizations, or anything that's relevant to your plugin.
|
||
|
||
### Plugin Structure
|
||
|
||
While developing your plugin, you can create your TypeScript or JavaScript files under the `src` directory. This includes creating services, endpoints, 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 endpoint in `src/api/index.js`, after running the `build` or `watch` commands [as defined earlier](#recommended-change-scripts), the file should be transpiled into `dist/api/index.js` in your plugin's root. You can alternative transpile them into the `api/index.js` in your plugin's root.
|
||
|
||
:::note
|
||
|
||
It was previously required to output your files into the root of the plugin's directory (for example, `api/index.js` instead of `dist/api/index.js`). As of v1.8, you can either have your files in the root of the directory or under the `dist` directory.
|
||
|
||
:::
|
||
|
||
### 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:
|
||
|
||
<DocCardList colSize={6} items={[
|
||
{
|
||
type: 'link',
|
||
href: '/development/entities/create',
|
||
label: 'Create an Entity',
|
||
customProps: {
|
||
icon: Icons['academic-cap-solid'],
|
||
description: 'Learn how to create an entity.'
|
||
}
|
||
},
|
||
{
|
||
type: 'link',
|
||
href: '/development/services/create-service',
|
||
label: 'Create a Service',
|
||
customProps: {
|
||
icon: Icons['academic-cap-solid'],
|
||
description: 'Learn how to create a service.'
|
||
}
|
||
},
|
||
{
|
||
type: 'link',
|
||
href: '/development/endpoints/create',
|
||
label: 'Create an Endpoint',
|
||
customProps: {
|
||
icon: Icons['academic-cap-solid'],
|
||
description: 'Learn how to create an endpoint.'
|
||
}
|
||
},
|
||
{
|
||
type: 'link',
|
||
href: '/development/events/create-subscriber',
|
||
label: 'Create a Subscriber',
|
||
customProps: {
|
||
icon: Icons['academic-cap-solid'],
|
||
description: 'Learn how to create a subscriber.'
|
||
}
|
||
},
|
||
{
|
||
type: 'link',
|
||
href: '/admin/widgets',
|
||
label: 'Create an Admin Widget',
|
||
customProps: {
|
||
icon: Icons['academic-cap-solid'],
|
||
description: 'Learn how to create an admin widget.',
|
||
badge: {
|
||
variant: 'orange',
|
||
children: 'Beta'
|
||
}
|
||
}
|
||
},
|
||
{
|
||
type: 'link',
|
||
href: '/admin/routes',
|
||
label: 'Create an Admin UI Route',
|
||
customProps: {
|
||
icon: Icons['academic-cap-solid'],
|
||
description: 'Learn how to create an admin UI route.',
|
||
badge: {
|
||
variant: 'orange',
|
||
children: 'Beta'
|
||
}
|
||
}
|
||
},
|
||
]} />
|
||
|
||
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.
|
||
|
||
<DocCardList colSize={6} items={[
|
||
{
|
||
type: 'link',
|
||
href: '/modules/carts-and-checkout/backend/add-payment-provider',
|
||
label: 'Create a Payment Processor',
|
||
customProps: {
|
||
icon: Icons['academic-cap-solid'],
|
||
description: 'Learn how to create a payment processor.'
|
||
}
|
||
},
|
||
{
|
||
type: 'link',
|
||
href: '/modules/carts-and-checkout/backend/add-fulfillment-provider',
|
||
label: 'Create a Fulfillment Provider',
|
||
customProps: {
|
||
icon: Icons['academic-cap-solid'],
|
||
description: 'Learn how to create a fulfillment provider.'
|
||
}
|
||
},
|
||
]} />
|
||
|
||
<DocCardList colSize={4} items={[
|
||
{
|
||
type: 'link',
|
||
href: '/development/search/create',
|
||
label: 'Create a Search Service',
|
||
customProps: {
|
||
icon: Icons['academic-cap-solid'],
|
||
description: 'Learn how to create a search service.'
|
||
}
|
||
},
|
||
{
|
||
type: 'link',
|
||
href: '/development/file-service/create-file-service',
|
||
label: 'Create a File Service',
|
||
customProps: {
|
||
icon: Icons['academic-cap-solid'],
|
||
description: 'Learn how to create a file service.'
|
||
}
|
||
},
|
||
{
|
||
type: 'link',
|
||
href: '/development/file-service/create-file-service',
|
||
label: 'Create a Notification Service',
|
||
customProps: {
|
||
icon: Icons['academic-cap-solid'],
|
||
description: 'Learn how to create a notification service.'
|
||
}
|
||
},
|
||
]} />
|
||
|
||
---
|
||
|
||
## Add Plugin Configuration
|
||
|
||
Plugins often allow developers that will later use them to enter their own configuration. For example, you can allow developers to specify the API key of a service you’re integrating.
|
||
|
||
To pass a plugin its configurations on a Medusa backend, you have to add it to the `plugins` array in `medusa-config.js`:
|
||
|
||
```js title=medusa-config.js
|
||
const plugins = [
|
||
// ...
|
||
{
|
||
resolve: `medusa-plugin-custom`,
|
||
options: {
|
||
name: "My Store",
|
||
},
|
||
},
|
||
]
|
||
```
|
||
|
||
Then, you can have access to your plugin configuration in the constructor of services in your plugin:
|
||
|
||
```js title=src/service/test.ts
|
||
// In a service in your plugin
|
||
class MyService extends TransactionBaseService {
|
||
constructor(container, options) {
|
||
super(container)
|
||
// options contains plugin configurations
|
||
this.name = options.name
|
||
}
|
||
// ...
|
||
}
|
||
```
|
||
|
||
You can also have access to the configurations in endpoints in your plugin:
|
||
|
||
```js title=src/api/index.ts
|
||
// in an endpoint in your plugin
|
||
export default (rootDirectory, options) => {
|
||
// options contain the plugin configurations
|
||
const router = Router()
|
||
|
||
router.get("/hello-world", (req, res) => {
|
||
res.json({
|
||
message:
|
||
`Welcome to ${options.name ? options.name : "Medusa"}!`,
|
||
})
|
||
})
|
||
|
||
return router
|
||
}
|
||
```
|
||
|
||
:::tip
|
||
|
||
Make sure to include in the README of your plugin the configurations 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.
|
||
|
||
You can pass the `enableUI` option to plugins as follows:
|
||
|
||
```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 configurations
|
||
const { enableUI, ...otherOptions } = options
|
||
// pass otherOptions to a third-party service
|
||
const client = new Client(otherOptions)
|
||
}
|
||
// ...
|
||
}
|
||
```
|
||
|
||
:::note
|
||
|
||
Since admin customizations are still in `beta` mode, `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 configuration for the customizations to appear in the admin dashboard. Once the admin customizations are out of beta, this behavior will be reversed.
|
||
|
||
:::
|
||
|
||
---
|
||
|
||
## Test Your Plugin
|
||
|
||
While you develop your plugin, you’ll need to test it on an actual Medusa backend. This can be done by using the [npm link](https://docs.npmjs.com/cli/v8/commands/npm-link) command.
|
||
|
||
In the root of your plugin directory, run the following command:
|
||
|
||
```bash npm2yarn
|
||
npm link
|
||
```
|
||
|
||
Then, change to the directory of the Medusa backend you want to test the plugin on and run the following command:
|
||
|
||
```bash npm2yarn
|
||
npm link medusa-plugin-custom
|
||
```
|
||
|
||
Where `medusa-plugin-custom` is the package name of your plugin.
|
||
|
||
After linking to your plugin in a local Medusa backend, either run the `build` or `watch` commands in your plugin directory:
|
||
|
||
```bash npm2yarn
|
||
# in the directory of the plugin
|
||
npm run watch
|
||
```
|
||
|
||
:::tip
|
||
|
||
If you’re running the `watch` command, you don’t need to run the `build` command every time you make a change to your plugin.
|
||
|
||
:::
|
||
|
||
Then, add your plugin into the array of plugins in `medusa-config.js`:
|
||
|
||
```js title=medusa-config.js
|
||
const plugins = [
|
||
// ...
|
||
{
|
||
resolve: `medusa-plugin-custom`,
|
||
// if your plugin has configurations
|
||
options: {
|
||
name: "My Store",
|
||
},
|
||
},
|
||
]
|
||
```
|
||
|
||
:::note
|
||
|
||
If your plugin has migrations, you must run them before you start the backend. Check out the [Migrations guide](../entities/migrations/overview.mdx#migrate-command) for more details.
|
||
|
||
:::
|
||
|
||
Finally, start your backend and test your plugin’s functionalities:
|
||
|
||
```bash npm2yarn
|
||
npm run start
|
||
```
|
||
|
||
### 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 <BACKEND_PATH>/node_modules/medusa-interfaces
|
||
npm link
|
||
cd <BACKEND_PATH>/node_modules/@medusajs/medusa
|
||
npm link
|
||
cd <PLUGIN_PATH>
|
||
rm -rf node_modules/medusa-interfaces
|
||
rm -rf node_modules/@medusajs/medusa
|
||
npm link medusa-interfaces
|
||
npm link @medusajs/medusa
|
||
npm link
|
||
cd <BACKEND_PATH>
|
||
npm link your-plugin
|
||
```
|
||
|
||
Where `<BACKEND_PATH>` is the path to your Medusa backend and `<PLUGIN_PATH>` 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 <PLUGIN_PATH>
|
||
rm -rf node_modules
|
||
cd <BACKEND_PATH>/node_modules/<PLUGIN_NAME>
|
||
npm install
|
||
cd <PLUGIN_PATH>
|
||
npm run build
|
||
cd <BACKEND_PATH>
|
||
npm run start
|
||
```
|
||
|
||
Where `<BACKEND_PATH>` is the path to your Medusa backend, `<PLUGIN_PATH>` is the path to your plugin and `<PLUGIN_NAME>` is the name of your plugin as it is in your plugin `package.json` file.
|
||
|
||
:::note
|
||
|
||
It is safe to ignore any `cross-env: command not found` error you may receive.
|
||
|
||
:::
|
||
|
||
---
|
||
|
||
## 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).
|