docs: publish restructure (#3496)

* docs: added features and guides overview page

* added image

* added version 2

* added version 3

* added version 4

* docs: implemented new color scheme

* docs: redesigned sidebar (#3193)

* docs: redesigned navbar for restructure (#3199)

* docs: redesigned footer (#3209)

* docs: redesigned cards (#3230)

* docs: redesigned admonitions (#3231)

* docs: redesign announcement bar (#3236)

* docs: redesigned large cards (#3239)

* docs: redesigned code blocks (#3253)

* docs: redesigned search modal and page (#3264)

* docs: redesigned doc footer (#3268)

* docs: added new sidebars + refactored css and assets (#3279)

* docs: redesigned api reference sidebar

* docs: refactored css

* docs: added code tabs transition

* docs: added new sidebars

* removed unused assets

* remove unusued assets

* Fix deploy errors

* fix incorrect link

* docs: fixed code responsivity + missing icons (#3283)

* docs: changed icons (#3296)

* docs: design fixes to the sidebar (#3297)

* redesign fixes

* docs: small design fixes

* docs: several design fixes after restructure (#3299)

* docs: bordered icon fixes

* docs: desgin fixes

* fixes to code blocks and sidebar scroll

* design adjustments

* docs: restructured homepage (#3305)

* docs: restructured homepage

* design fixes

* fixed core concepts icon

* docs: added core concepts page (#3318)

* docs: restructured homepage

* design fixes

* docs: added core concepts page

* changed text of different components

* docs: added architecture link

* added missing prop for user guide

* docs: added regions overview page (#3327)

* docs: added regions overview

* moved region pages to new structure

* docs: fixed description of regions architecture page

* small changes

* small fix

* docs: added customers overview page (#3331)

* docs: added regions overview

* moved region pages to new structure

* docs: fixed description of regions architecture page

* small changes

* small fix

* docs: added customers overview page

* fix link

* resolve link issues

* docs: updated regions architecture image

* docs: second-iteration fixes (#3347)

* docs: redesigned document

* design fixes

* docs: added products overview page (#3354)

* docs: added carts overview page (#3363)

* docs: added orders overview (#3364)

* docs: added orders overview

* added links in overview

* docs: added vercel redirects

* docs: added soon badge for cards (#3389)

* docs: resolved feedback changes + organized troubleshooting pages (#3409)

* docs: resolved feedback changes

* added extra line

* docs: changed icons for restructure (#3421)

* docs: added taxes overview page (#3422)

* docs: added taxes overview page

* docs: fix sidebar label

* added link to taxes overview page

* fixed link

* docs: fixed sidebar scroll (#3429)

* docs: added discounts overview (#3432)

* docs: added discounts overview

* fixed links

* docs: added gift cards overview (#3433)

* docs: added price lists overview page (#3440)

* docs: added price lists overview page

* fixed links

* docs: added sales channels overview page (#3441)

* docs: added sales overview page

* fixed links

* docs: added users overview (#3443)

* docs: fixed sidebar border height (#3444)

* docs: fixed sidebar border height

* fixed svg markup

* docs: added possible solutions to feedback component (#3449)

* docs: added several overview pages + restructured files (#3463)

* docs: added several overview pages

* fixed links

* docs: added feature flags + PAK overview pages (#3464)

* docs: added feature flags + PAK overview pages

* fixed links

* fix link

* fix link

* fixed links colors

* docs: added strategies overview page (#3468)

* docs: automated upgrade guide (#3470)

* docs: automated upgrade guide

* fixed vercel redirect

* docs: restructured files in docs codebase (#3475)

* docs: restructured files

* docs: fixed eslint exception

* docs: finished restructure loose-ends (#3493)

* fixed uses of backend

* docs: finished loose ends

* eslint fixes

* fixed links

* merged master

* added update instructions for v1.7.12
This commit is contained in:
Shahed Nasser
2023-03-16 17:03:10 +02:00
committed by GitHub
parent f312ce1e0f
commit 1decaa27c7
415 changed files with 12422 additions and 5098 deletions

View File

@@ -0,0 +1,391 @@
---
description: 'Learn about the different configurations available in a Medusa backend. This includes configurations related to the database, CORS, plugins, redis, and more.'
---
# Configure Medusa Backend
In this document, youll learn what configurations you can add to your Medusa backend and how to add them.
## Prerequisites
This document assumes you already followed along with the [“Set up your development environment” documentation](./prepare-environment.mdx) and have [installed a Medusa backend](./install.mdx#create-a-medusa-backend).
---
## Medusa Configurations File
The configurations for your Medusa backend are in `medusa-config.js`. This includes database, Redis, and plugin configurations, among other configurations.
Some of the configurations mentioned in this document are already defined in `medusa-config.js` with default values. Its important that you know what these configurations are used for and how to set them.
---
## Environment Variables
In your configurations, youll often use environment variables. For example, when using API keys or setting your database URL.
By default, Medusa loads environment variables from the systems environment variables. Any different method you prefer to use or other location youd prefer to load environment variables from you need to manually implement.
:::info
This change in how environment variables are loaded was introduced in version 1.3.0. You can learn more in the [upgrade guide for version 1.3.0](../../upgrade-guides/medusa-core/1-3-0.md).
:::
---
## Database Configuration
Medusa supports two database types: SQLite and PostgreSQL.
:::tip
You can use SQLite for development purposes, however, its recommended to use PostgreSQL.
:::
### SQLite Configurations
For SQLite you mainly need two configurations:
```jsx
module.exports = {
projectConfig: {
// ...other configurations
database_type: "sqlite",
database_database: "./medusa-db.sql",
},
}
```
Where `database_type` is `sqlite` and `database_database` is the location you want the SQLite database to be created in.
### PostgreSQL Configurations
:::note
Before getting started with configuring PostgreSQL, you should have created a PostgreSQL database. You can check how to create a database in [PostgreSQL's documentation](https://www.postgresql.org/docs/current/sql-createdatabase.html).
:::
For PostgreSQL you mainly need two configurations:
```jsx
module.exports = {
projectConfig: {
// ...other configurations
database_type: "postgres",
database_url: DATABASE_URL,
},
}
```
Where `database_type` is `postgres` and `DATABASE_URL` is the URL connection string to your PostgreSQL database. You can check out how to format it in [PostgreSQLs documentation](https://www.postgresql.org/docs/current/libpq-connect.html).
It's recommended to set the Database URL as an environment variable:
```bash
DATABASE_URL=<YOUR_DATABASE_URL>
```
Where `<YOUR_DATABASE_URL>` is the URL of your PostgreSQL database.
### Changing PostgreSQL Schema
By default, the `public` schema is used in PostgreSQL. You can change it to use a custom schema by passing the `search_path` option in the database URL. For example:
```bash
postgres://localhost/store?options=-c search_path=test
```
Where `test` is the name of the database schema that should be used instead of `public`.
### Changing Database Type
Remember to run migrations after you change your database type to `postgres` from another type:
```bash
medusa migrations run
```
### Common Configuration
As Medusa internally uses [Typeorm](https://typeorm.io/) to connect to the database, the following configurations are also available:
1. `database_logging`: enable or disable logging.
2. `database_extra`: extra options that you can pass to the underlying database driver.
These configurations are not required and can be omitted.
```jsx
module.exports = {
projectConfig: {
// ...other configurations
database_logging: true,
database_extra: {},
},
}
```
---
## Redis
Medusa uses Redis to handle the event queue, among other usages. You need to set Redis URL in the configurations:
```jsx
module.exports = {
projectConfig: {
// ...other configurations
redis_url: REDIS_URL,
},
}
```
Where `REDIS_URL` is the URL used to connect to Redis. The format of the connection string is `redis[s]://[[username][:password]@][host][:port][/db-number]`.
If you omit this configuration, events will not be emitted and subscribers will not work.
:::tip
By default, the Redis connection string should be `redis://localhost:6379` unless you made any changes to the default configurations during the installation.
:::
It is recommended to set the Redis URL as an environment variable:
```bash
REDIS_URL=<YOUR_REDIS_URL>
```
Where `<YOUR_REDIS_URL>` is the URL of your Redis backend.
:::info
You can learn more about Subscribers and events in the [Subscriber documentation](../events/create-subscriber.md).
:::
---
## JWT Secret
Medusa uses JSON Web Token (JWT) to handle user authentication. To set the JWT secret:
```jsx
module.exports = {
projectConfig: {
// ...other configurations
jwt_secret: "very secure string",
},
}
```
Where `jwt_secret` is the secret used to create the tokens. The more secure it is the better.
It is recommended to set the JWT Secret as an environment variable:
```bash
JWT_SECRET=<YOUR_JWT_SECRET>
```
Where `<YOUR_JWT_SECRET>` is the JWT secret you want to use.
:::caution
In a development environment, if this option is not set the default secret is “supersecret”. However, in production, if this option is not set an error will be thrown and your backend will crash.
:::
---
## Cookie Secret
This configuration is used to sign the session ID cookie. To set the cookie secret:
```jsx
module.exports = {
projectConfig: {
// ...other configurations
cookie_secret: "very secure string",
},
}
```
Where `cookie_secret` is the secret used to create the tokens. The more secure it is the better.
It is recommended to set the Cookie secret as an environment variable:
```bash
COOKIE_SECRET=<YOUR_COOKIE_SECRET>
```
Where `<YOUR_COOKIE_SECRET>` is the Cookie secret you want to use.
:::caution
In a development environment, if this option is not set the default secret is “supersecret”. However, in production, if this option is not set an error will be thrown and your backend will crash.
:::
---
## CORS Configurations
Medusa uses Cross-Origin Resource Sharing (CORS) to only allow specific origins to access the backend.
The Admin and the Storefront have different CORS configurations that must be configured.
### Accepted Patterns
For both of the Admin and the Storefront CORS configurations, the value is expected to be a string. This string can be a comma-separated list of accepted origins. Every origin in that list can be of the following types:
1. The accepted origin as is. For example, `http://localhost:8000`.
2. A regular expression pattern that can match more than one origin. For example, `*.example.com`. The regex pattern that the backend tests for is `^([\/~@;%#'])(.*?)\1([gimsuy]*)$`.
Here are some examples of common use cases:
```bash
# Allow different ports locally starting with 700
ADMIN_CORS=/http:\/\/localhost:700\d+$/
# Allow any origin ending with vercel.app. For example, storefront.vercel.app
STORE_CORS=/vercel\.app$/
# Allow all HTTP requests
ADMIN_CORS=/http:\/\/*/
```
Although this is not recommended, but when setting these values directly in `medusa-config.json`, make sure to add an extra escaping `backslash` for every backslash in the pattern. For example:
```js
const ADMIN_CORS = process.env.ADMIN_CORS ||
"/http:\\/\\/localhost:700\\d+$/"
```
:::tip
The examples above apply to both Admin and Store CORS.
:::
### Admin CORS
To make sure your Admin dashboard can access the Medusa backends admin endpoints, set this configuration:
```jsx
module.exports = {
projectConfig: {
// ...other configurations
admin_cors: ADMIN_CORS,
},
}
```
Where `ADMIN_CORS` is the URL of your admin dashboard. By default, its `http://localhost:7000,http://localhost:7001`.
It is recommended to set the Admin CORS as an environment variable:
```bash
ADMIN_CORS=<YOUR_ADMIN_CORS>
```
Where `<YOUR_ADMIN_CORS>` is the URL of your admin dashboard.
:::tip
Make sure that the URL is without a backslash at the end. For example, you should use `http://localhost:7000` instead of `http://localhost:7000/`.
:::
### Storefront CORS
To make sure your Storefront dashboard can access the Medusa backend, set this configuration:
```jsx
module.exports = {
projectConfig: {
// ...other configurations
store_cors: STORE_CORS,
},
}
```
Where `STORE_CORS` is the URL of your storefront. By default, its `http://localhost:8000`.
It is recommended to set the Storefront CORS as an environment variable:
```bash
STORE_CORS=<YOUR_STORE_CORS>
```
Where `<YOUR_STORE_CORS>` is the URL of your storefront.
:::tip
Make sure that the URL is without a backslash at the end. For example, you should use `http://localhost:8000` instead of `http://localhost:8000/`.
:::
---
## Plugins
On your Medusa backend, you can use Plugins to add custom features or integrate third-party services. For example, installing a plugin to use Stripe as a payment provider.
:::info
You can learn more about plugins in the [Plugins Overview documentation](../plugins/overview.mdx).
:::
Aside from installing the plugin with NPM, you need to pass the plugin you installed into the `plugins` array defined in `medusa-config.js`. This array is then exported along with other configurations youve added:
```jsx
module.exports = {
projectConfig: {
// previous configurations mentioned...
},
plugins,
}
```
### Add a Plugin Without Configuration
To add a plugin that doesnt need any configurations, you can simply add its name to the `plugins` array:
```jsx
const plugins = [
// other plugins...
`medusa-my-plugin`,
]
```
### Add a Plugin With Configuration
To add a plugin with configurations, you need to add an object to the `plugins` array with the plugins name and configurations:
```jsx
const plugins = [
// other plugins...
{
resolve: `medusa-my-plugin`,
options: {
apiKey: `test`,
},
},
]
```
:::tip
It is recommended to use environment variables to store values of options instead of hardcoding them in `medusa-config.js`.
:::
---
## See Also
- [Medusa architecture overview](../fundamentals/architecture-overview.md)
- [Plugins](../plugins/overview.mdx)

View File

@@ -0,0 +1,11 @@
.osTabs {
background-color: var(--ifm-note-background);
padding: 10px;
border: 1px solid var(--ifm-note-border-color);
border-radius: var(--ifm-global-radius);
color: var(--ifm-alert-foreground-color);
}
.osTabs li:not([aria-selected=true]) {
color: var(--ifm-color-content);
}

View File

@@ -0,0 +1,104 @@
---
description: 'This quickstart guide will help you set up a Medusa backend in three steps.'
addHowToData: true
---
import Feedback from '@site/src/components/Feedback';
# Install Medusa Backend
This document will guide you through setting up your Medusa backend in a three steps.
## Prerequisites
### Node.js
Medusa supports Node versions 14 and 16. You can check which version of Node you have by running the following command:
```bash noReport
node -v
```
You can install Node from the [official website](https://nodejs.org/en/).
### Git
Git is required for this setup. You can find instructions on how to install it from the [Set up your dev environment documentation](../../development/backend/prepare-environment.mdx#git).
---
## Create a Medusa Backend
:::tip
It is recommended to use [Yarn](https://yarnpkg.com/getting-started/install) for the installation process as it's much faster than using NPM.
:::
### 1. Install Medusa CLI
```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).
:::
### 2. Create a new Medusa project
```bash noReport
medusa new my-medusa-store --seed
```
### 3. Start your Medusa backend
```bash noReport
cd my-medusa-store
medusa develop
```
<Feedback
event="survey_server_quickstart"
question="Did you set up the backend successfully?"
positiveQuestion="Is there anything that should improved?"
negativeQuestion="Please describe the issue you faced."
/>
### Test the Backend
After these three steps and in only a couple of minutes, you now have a complete commerce engine running locally. You can test it out by sending a request using a tool like Postman or through the command line:
```bash noReport
curl localhost:9000/store/products
```
---
## Next Steps
### Learn about the Architecture
Medusa backend is made up of different resources that make it a powerful server. Get an overview of them in the [Medusa Architecture Overview](../fundamentals/architecture-overview.md) documentation.
### Set Up Development Environment
For an optimal experience developing with Medusa and to make sure you can use its advanced functionalities, you'll need to install more tools such as Redis or PostgreSQL.
Follow [this documentation to learn how to set up your development environment](../../development/backend/prepare-environment.mdx).
### Backend Configurations
It's important to configure your Medusa backend properly and learn how environment variables are loaded.
You can learn more about configuring your backend and loading environment variables in the [Configure your Backend documentation](../../development/backend/configurations.md).
### File Service Plugin
To upload product images to your Medusa backend, you must install and configure one of the following file service plugins:
- [MinIO](../../plugins/file-service/minio.md) (Recommended for local development)
- [S3](../../plugins/file-service/s3.md)
- [DigitalOcean Spaces](../../plugins/file-service/spaces.md)

View File

@@ -0,0 +1,288 @@
---
description: 'Learn how to prepare your development environment while using Medusa. This guide includes how to install Node.js, Git, Medusa CLI tool, PostgreSQL, and Redis.'
---
import styles from './development.module.css';
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
# Prepare Development Environment
This document will guide you to prepare your development environment to efficiently and properly use Medusa.
## Required Tools
To get your development environment ready you need to install the following tools:
### Node.js
Node.js is the environment that makes it possible for Medusa to run, so you must install Node.js on your computer to start Medusa development.
:::caution
Medusa supports versions 14 and 16 of Node.js. You can check your Node.js version using the following command:
```bash noReport
node -v
```
:::
<Tabs groupId="operating-systems" wrapperClassName={styles.osTabs}>
<TabItem value="windows" label="Windows" default>
You can install the executable directly from [the Node.js website](https://nodejs.org/en/#home-downloadhead).
For other approaches, you can check out [Node.jss guide](https://nodejs.org/en/download/package-manager/#windows-1).
</TabItem>
<TabItem value="linux" label="Linux">
You can use the following commands to install Node.js on Ubuntu:
```bash
#Ubuntu
sudo apt update
sudo apt install nodejs
```
For other Linux distributions, you can check out [Node.jss guide](https://nodejs.org/en/download/package-manager/).
</TabItem>
<TabItem value="macos" label="macOS">
You can use the following commands to install Node.js on macOS:
<Tabs groupId="homebrew" wrapperClassName='code-tabs'>
<TabItem value="homebrew" label="Homebrew">
```bash
brew install node
```
</TabItem>
<TabItem value="no-homebrew" label="Without Homebrew">
```bash
curl \
"https://nodejs.org/dist/latest/node-${VERSION:-$(wget -qO- \
https://nodejs.org/dist/latest/ | sed -nE \
's|.*>node-(.*)\.pkg</a>.*|\1|p')}.pkg" \
> "$HOME/Downloads/node-latest.pkg" &&
sudo installer -store -pkg "$HOME/Downloads/node-latest.pkg" -target "/"
```
</TabItem>
</Tabs>
For other approaches, you can check out [Node.jss guide](https://nodejs.org/en/download/package-manager/#macos).
:::tip
Make sure that you have Xcode command line tools installed; if not, run the following command to install it: `xcode-select --install`
:::
</TabItem>
</Tabs>
### Git
Medusa uses Git behind the scenes when you create a new project so you'll have to install it on your computer to get started.
<Tabs groupId="operating-systems" wrapperClassName={styles.osTabs}>
<TabItem value="windows" label="Windows" default>
To install Git on Windows, you need to [download the installable package](https://git-scm.com/download/win).
</TabItem>
<TabItem value="linux" label="Linux">
For Debian/Ubuntu, you can use the following command:
```bash
apt-get install git
```
As for other Linux distributions, please check [gits guide](https://git-scm.com/download/linux).
</TabItem>
<TabItem value="macos" label="macOS">
You should already have Git installed as part of the Xcode command-line tools.
However, if for any reason you need to install it manually, you can install it with Homebrew:
```bash
brew install git
```
You can also check out [gits guide](https://git-scm.com/download/mac) for more installation options.
</TabItem>
</Tabs>
### Medusa CLI
The final installation required to get started with Medusa is the Medusa CLI. It is an NPM package you can install globally on your machine to get instant access to commands that help you manage and run your Medusa project.
You can install Medusas CLI with the following command:
```bash npm2yarn
npm install @medusajs/medusa-cli -g
```
You can check that Medusa was installed by running the following command:
```bash noReport
medusa -v
```
:::note
If you run into any errors while installing the CLI tool, check out the [troubleshooting guide](../../troubleshooting/cli-installation-errors.mdx).
:::
---
## Install Medusa Backend
If you're not interested in installing the optional tools and want to get started with Medusa quickly, check out the [Medusa Backend Quickstart](./install.mdx).
---
## Optional Tools
These tools are not required to have to run a Medusa backend, but it's highly recommended that you have them installed.
### PostgreSQL
Although you can use an SQLite database with Medusa which would require no necessary database installations, it is recommended to use a PostgreSQL database for your backend.
:::tip
After installing PostgreSQL, check out the [Configure your Backend documentation](./configurations.md#postgresql-configurations) to learn how to configure PostgreSQL to work with Medusa.
:::
<Tabs groupId="operating-systems" wrapperClassName={styles.osTabs}>
<TabItem value="windows" label="Windows">
You can [download the PostgreSQL Windows installer](https://www.postgresql.org/download/windows/) from their website.
</TabItem>
<TabItem value="linux" label="Linux">
If youre using Ubuntu, you can use the following commands to download and install PostgreSQL:
```bash
sudo sh -c \
'echo "deb http://apt.postgresql.org/pub/repos/apt $(lsb_release -cs)-pgdg main" > /etc/apt/sources.list.d/pgdg.list'
wget --quiet -O - \
https://www.postgresql.org/media/keys/ACCC4CF8.asc | sudo apt-key add -
sudo apt-get update
sudo apt-get -y install postgresql
```
For other distributions, you can check out [PostgreSQLs website for more guides](https://www.postgresql.org/download/linux/).
</TabItem>
<TabItem value="macos" label="macOS">
You can download PostgreSQL on your macOS using [the installer on their website](https://www.postgresql.org/download/macosx/).
</TabItem>
<TabItem value="docker" label="Docker">
Make sure the Docker Desktop app is running, then run the following command to quickly spin up a PostgreSQL instance:
```bash
docker run --name postgres -e POSTGRES_USER=postgres \
-e POSTGRES_PASSWORD=postgres -e \
POSTGRES_DB=medusa-docker -p 5432:5432 -d postgres
```
Where:
- `--name` creates a new container named `postgres`.
- `-e` creates environment variables `POSTGRES_USER`, `POSTGRES_PASSWORD` and `POSTGRES_DB`. These will be used to set up a database named `medusa-docker` with the username and password being `postgres`.
- `-p` maps the container port `5432` to the external host `5432`. This allows you to connect to the database backend from outside of the container.
- `-d` enables Docker to run the container in the background.
- The last section of the command, `postgres` grabs the latest postgres image from the Docker hub.
</TabItem>
</Tabs>
### Redis
Medusa uses Redis as the event queue in the backend. If you want to use subscribers to handle events such as when an order is placed and perform actions based on the events, then you need to install and configure Redis.
If you dont install and configure Redis with your Medusa backend, then it will work without any events-related features.
:::tip
After installing Redis, check out the [Configure your Backend documentation](./configurations.md#redis) to learn how to configure Redis to work with Medusa.
:::
<Tabs groupId="operating-systems" wrapperClassName={styles.osTabs}>
<TabItem value="windows" label="Windows">
To use Redis on Windows, you must have [Windows Subsystem for Linux (WSL2) enabled](https://docs.microsoft.com/en-us/windows/wsl/install). This lets you run Linux binaries on Windows.
After installing and enabling WSL2, if you use an Ubuntu distribution you can run the following commands to install Redis:
```bash
sudo apt-add-repository ppa:redislabs/redis
sudo apt-get update
sudo apt-get upgrade
sudo apt-get install redis-server
## Start Redis server
sudo service redis-server start
```
</TabItem>
<TabItem value="linux" label="Linux">
If you use Ubuntu you can use the following commands to install Redis:
```bash
curl -fsSL https://packages.redis.io/gpg | \
sudo gpg --dearmor -o /usr/share/keyrings/redis-archive-keyring.gpg
echo "deb [signed-by=/usr/share/keyrings/redis-archive-keyring.gpg] https://packages.redis.io/deb $(lsb_release -cs) main" \
| sudo tee /etc/apt/sources.list.d/redis.list
sudo apt-get update
sudo apt-get install redis
## Start Redis server
sudo service redis-server start
```
For other distributions, you can check out [Redis guide on this](https://redis.io/docs/getting-started/installation/install-redis-on-linux/).
</TabItem>
<TabItem value="macos" label="macOS">
You can install Redis on macOS using Homebrew with the following command:
```bash
brew install redis
## Start Redis server
brew services start redis
```
To install Redis without Homebrew you can check out [Rediss guide on installing it from source](https://redis.io/docs/getting-started/installation/install-redis-from-source/).
</TabItem>
</Tabs>
---
## See Also
- [Install Medusa backend](./install.mdx)
- [Configure the Medusa backend](./configurations.md)

View File

@@ -0,0 +1,463 @@
---
description: 'Learn how to create a batch job strategy in Medusa. This guide also includes how to test your batch job strategy.'
addHowToData: true
---
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
# Create a Batch Job Strategy
In this document, youll learn how to create a batch job strategy in Medusa.
:::info
If youre interested to learn more about what Batch Jobs are and how they work, check out [this documentation](./index.mdx).
:::
## Overview
Batch jobs can be used to perform long tasks in the background of your Medusa backend. Batch jobs are handled by batch job strategies. An example of a batch job strategy is the Import Products functionality.
This documentation helps you learn how to create a batch job strategy. The batch job strategy used in this example changes the status of all draft products to `published`.
---
## Prerequisites
### Medusa Components
It is assumed that you already have a Medusa backend installed and set up. If not, you can follow our [quickstart guide](../backend/install.mdx) to get started.
### Redis
Redis is required for batch jobs to work. Make sure you [install Redis](../backend/prepare-environment.mdx#redis) and [configure it with your Medusa backend](../backend/configurations.md#redis).
### PostgreSQL
If you use SQLite during your development, its highly recommended that you use PostgreSQL when working with batch jobs. Learn how to [install PostgreSQL](../backend/prepare-environment.mdx#postgresql) and [configure it with your Medusa backend](../backend/configurations.md#postgresql-configurations).
---
## 1. Create a File
A batch job strategy is essentially a class defined in a TypeScript or JavaScript file. You should create this file in `src/strategies`.
Following the example used in this documentation, create the file `src/strategies/publish.ts`.
---
## 2. Create Class
Batch job strategies must extend the abstract class `AbstractBatchJobStrategy` and implement its abstract methods.
Add the following content to the file you created:
```ts title=src/strategies/publish.ts
import {
AbstractBatchJobStrategy,
BatchJobService,
} from "@medusajs/medusa"
import { EntityManager } from "typeorm"
class PublishStrategy extends AbstractBatchJobStrategy {
protected batchJobService_: BatchJobService
processJob(batchJobId: string): Promise<void> {
throw new Error("Method not implemented.")
}
buildTemplate(): Promise<string> {
throw new Error("Method not implemented.")
}
protected manager_: EntityManager
protected transactionManager_: EntityManager
}
export default PublishStrategy
```
---
## 3. Define Required Properties
A batch job strategy class must have two static properties: the `identifier` and `batchType` properties. The `identifier` must be a unique string associated with your batch job strategy, and `batchType` must be the batch job's type.
You will use the `batchType` later when you [interact with the Batch Job APIs](#test-your-batch-job-strategy).
Following the same example, add the following properties to the `PublishStrategy` class:
```ts
class PublishStrategy extends AbstractBatchJobStrategy {
static identifier = "publish-products-strategy"
static batchType = "publish-products"
// ...
}
```
---
## 4. Define Methods
### (Optional) prepareBatchJobForProcessing
Medusa runs this method before it creates the batch job to prepare the content of the batch job record in the database. It accepts two parameters: the batch job data sent in the body of the [Create Batch Job request](/api/admin/#tag/Batch-Job/operation/PostBatchJobs), and the request instance.
Implementing this method is optional. For example:
```ts
class PublishStrategy extends AbstractBatchJobStrategy {
// ...
async prepareBatchJobForProcessing(
batchJob: CreateBatchJobInput,
req: Express.Request
): Promise<CreateBatchJobInput> {
// make changes to the batch job's fields...
return batchJob
}
}
```
### (Optional) preProcessBatchJob
Medusa runs this method after it creates the batch job, but before it is confirmed and processed. You can use this method to perform any necessary action before the batch job is processed. You can also use this method to add information related to the expected result.
For example, this implementation of the `preProcessBatchJob` method calculates how many draft products it will published and adds it to the `result` attribute of the batch job:
```ts
class PublishStrategy extends AbstractBatchJobStrategy {
// ...
async preProcessBatchJob(batchJobId: string): Promise<void> {
return await this.atomicPhase_(
async (transactionManager) => {
const batchJob = (await this.batchJobService_
.withTransaction(transactionManager)
.retrieve(batchJobId))
const count = await this.productService_
.withTransaction(transactionManager)
.count({
status: ProductStatus.DRAFT,
})
await this.batchJobService_
.withTransaction(transactionManager)
.update(batchJob, {
result: {
advancement_count: 0,
count,
stat_descriptors: [
{
key: "product-publish-count",
name: "Number of products to publish",
message:
`${count} product(s) will be published.`,
},
],
},
})
})
}
}
```
The `result` attribute is an object that can hold many properties including:
- `count`: used to indicate how many items (in this case, products) that the task will run on.
- `advancement_count`: used to indicate the current number of items processed at a given moment. Since the batch job isn't processed yet, you set it to zero.
- `stat_descriptors`: can be used to show human-readable messages.
### processJob
Medusa runs this method to process the batch job once it is confirmed.
For example, this implementation of the `processJob` method retrieves all draft products and changes their status to published:
```ts
class PublishStrategy extends AbstractBatchJobStrategy {
// ...
async processJob(batchJobId: string): Promise<void> {
return await this.atomicPhase_(
async (transactionManager) => {
const productServiceTx = this.productService_
.withTransaction(transactionManager)
const productList = await productServiceTx
.list({
status: [ProductStatus.DRAFT],
})
productList.forEach(async (product: Product) => {
await productServiceTx
.update(product.id, {
status: ProductStatus.PUBLISHED,
})
})
await this.batchJobService_
.withTransaction(transactionManager)
.update(batchJobId, {
result: {
advancement_count: productList.length,
},
})
}
)
}
}
```
:::note
When a batch job is canceled, the processing of the batch job doesnt automatically stop. You will have to manually check for changes in the status of the batch job. For example, you can retrieve the batch job and use the condition `batchJob.status === BatchJobStatus.CANCELED` to check if the batch job was canceled.
:::
### buildTemplate
This method can be used in cases where you provide a template file to download, such as when implementing an import or export functionality.
If not necessary to your use case, you can simply return an empty string:
```ts
class PublishStrategy extends AbstractBatchJobStrategy {
// ...
async buildTemplate(): Promise<string> {
return ""
}
}
```
### (Optional) shouldRetryOnProcessingError
Medusa uses this method to decide whether it should retry the batch job if an error occurs during processing.
By default, the `AbstractBatchJobStrategy` class implements this method and returns `false`, indicating that if a batch jobs process fails it will not be retried.
If you would like to change that behavior, you can override this method to return a different value:
```ts
class PublishStrategy extends AbstractBatchJobStrategy {
// ...
protected async shouldRetryOnProcessingError(
batchJob: BatchJob,
err: unknown
): Promise<boolean> {
return true
}
}
```
### (Optional) handleProcessingError
Medusa uses this method to handle errors that occur during processing. By default, it changes the status of the batch job to `failed` and sets the `errors` property of the batch jobs `result` attribute.
You can use this method as implemented in `AbstractBatchJobStrategy` at any point in your batch job process to set the batch job as failed.
You can also override this method in your batch job strategy and change how it works:
```ts
class PublishStrategy extends AbstractBatchJobStrategy {
// ...
protected async handleProcessingError<T>(
batchJobId: string,
err: unknown,
result: T
): Promise<void> {
// different implementation...
}
}
```
---
## 5. Run Build Command
After you create the batch job and before testing it out, you must run the build command in the directory of your Medusa backend:
```bash
npm run build
```
---
## Test your Batch Job Strategy
This section covers how to test and use your batch job strategy. Make sure to start your backend first:
```bash
npm run start
```
:::info
If your `start` script uses the `medusa develop` command, whenever you make changes in the `src` directory the `build` command will automatically run and the backend will restart.
:::
You must also use an authenticated user to send batch job requests. You can refer to the [authentication guide in the API reference](/api/admin/#section/Authentication) for more details.
If you follow along with the JS Client code snippets, make sure to [install and set it up first](../../js-client/overview.md).
### Create Batch Job
The first step is to create a batch job using the [Create Batch Job endpoint](/api/admin/#tag/Batch-Job/operation/PostBatchJobs). In the body of the request, you must set the `type` to the value of `batchType` in the batch job strategy you created.
For example, this creates a batch job of the type `publish-products`:
<Tabs groupId="request-types" wrapperClassName="code-tabs">
<TabItem value="client" label="Medusa JS Client" default>
```jsx
medusa.admin.batchJobs.create({
type: "publish-products",
context: { },
dry_run: true,
})
.then(( batch_job ) => {
console.log(batch_job.status)
})
```
</TabItem>
<TabItem value="fetch" label="Fetch API">
```jsx
fetch(`<BACKEND_URL>/admin/batch-jobs`, {
method: "POST",
credentials: "include",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
type: "publish-products",
context: { },
dry_run: true,
}),
})
.then((response) => response.json())
.then(({ batch_job }) => {
console.log(batch_job.status)
})
```
</TabItem>
<TabItem value="curl" label="cURL">
```bash
curl -L -X POST '<BACKEND_URL>/admin/batch-jobs' \
-H 'Authorization: Bearer <API_TOKEN>' \
-H 'Content-Type: application/json' \
--data-raw '{
"type": "publish-products",
"context": { },
"dry_run": true
}'
```
</TabItem>
</Tabs>
You set the `dry_run` to `true` to disable automatic confirmation and running of the batch job. If you want the batch job to run automatically, you can remove this body parameter.
Make sure to replace `<BACKEND_URL>` with the backend URL where applicable.
### (Optional) Retrieve Batch Job
You can retrieve the batch job afterward to get its status and view details about the process in the `result` property:
<Tabs groupId="request-type" wrapperClassName="code-tabs">
<TabItem value="client" label="Medusa JS Client" default>
```jsx
medusa.admin.batchJobs.retrieve(batchJobId)
.then(( batch_job ) => {
console.log(batch_job.status, batch_job.result)
})
```
</TabItem>
<TabItem value="fetch" label="Fetch API">
```jsx
fetch(`<BACKEND_URL>/admin/batch-jobs/${batchJobId}`, {
credentials: "include",
})
.then((response) => response.json())
.then(({ batch_job }) => {
console.log(batch_job.status, batch_job.result)
})
```
</TabItem>
<TabItem value="curl" label="cURL">
```bash
curl -L -X GET '<BACKEND_URL>/admin/batch-jobs/<BATCH_JOB_ID>' \
-H 'Authorization: Bearer <API_TOKEN>'
# <BATCH_JOB_ID> is the ID of the batch job
```
</TabItem>
</Tabs>
Based on the batch job strategy implemented in this documentation, the `result` property could be something like this:
```json noReport
"result": {
"count": 1,
"stat_descriptors": [
{
"key": "product-publish-count",
"name": "Number of products to publish",
"message": "1 product(s) will be published."
}
],
"advancement_count": 0
},
```
### Confirm Batch Job
To process the batch job, send a request to [confirm the batch job](/api/admin/#tag/Batch-Job/operation/PostBatchJobsBatchJobConfirmProcessing):
<Tabs groupId="request-type" wrapperClassName="code-tabs">
<TabItem value="client" label="Medusa JS Client" default>
```jsx
medusa.admin.batchJobs.confirm(batchJobId)
.then(( batch_job ) => {
console.log(batch_job.status)
})
```
</TabItem>
<TabItem value="fetch" label="Fetch API">
```jsx
fetch(`<BACKEND_URL>/admin/batch-jobs/${batchJobId}/confirm`, {
method: "POST",
credentials: "include",
})
.then((response) => response.json())
.then(({ batch_job }) => {
console.log(batch_job.status)
})
```
</TabItem>
<TabItem value="curl" label="cURL">
```bash
curl -L -X POST '<BACKEND_URL>/admin/batch-jobs/<BATCH_JOB_ID>/confirm' \
-H 'Authorization: Bearer <API_TOKEN>'
# <BATCH_JOB_ID> is the ID of the batch job
```
</TabItem>
</Tabs>
The batch job will start processing afterward. Based on the batch job strategy implemented in this documentation, draft products will be published.
You can [retrieve the batch job](#optional-retrieve-batch-job) at any given point to check its status.

View File

@@ -0,0 +1,130 @@
---
description: 'Learn how to customize the import strategy in Medusa. The import strategy can be used to import entities such as products, prices in a price list, orders, or other entities.'
addHowToData: true
---
# How to Customize Import Strategy
In this document, youll learn how to create a custom product import strategy either by overwriting the default strategy or creating your own.
## Overview
Product Import Strategy is essentially a batch job strategy. Medusa provides the necessary mechanisms to overwrite or create your own strategy.
Although this documentation specifically targets import strategies, you can use the same steps to overwrite any batch job strategy in Medusa, including export strategies.
---
## Prerequisites
### Medusa Components
It's assumed that you already have a Medusa backend installed and set up. If not, you can follow our [quickstart guide](../backend/install.mdx) to get started.
### Redis
Redis is required for batch jobs to work. Make sure you [install Redis](../backend/prepare-environment.mdx#redis) and [configure it with your Medusa backend](../backend/configurations.md#redis).
### PostgreSQL
If you use SQLite during your development, its highly recommended that you use PostgreSQL when working with batch jobs. Learn how to [install PostgreSQL](../backend/prepare-environment.mdx#postgresql) and [configure it with your Medusa backend](../backend/configurations.md#postgresql-configurations).
---
## Overwrite Batch Job Strategy
The steps required for overwriting a batch job strategy are essentially the same steps required to create a batch job strategy with a minor difference. For that reason, this documentation does not cover the basics of a batch job strategy.
If youre interested to learn more about batch job strategies and how they work, please check out the [Create Batch Job Strategy documentation](./create.mdx).
### 1. Create a File
You must store batch job strategies in the `src/strategies` directory of your Medusa backend. They are either TypeScript or JavaScript files.
So, for example, you can create the file `src/strategies/import.ts`.
### 2. Create a Class
The batch job strategy class must extend the `AbstractBatchJobStrategy` class which you can import from Medusas core repository.
For example, you can define the following class in the file you created:
```ts title=src/strategies/import.ts
import {
AbstractBatchJobStrategy,
BatchJobService,
} from "@medusajs/medusa"
import { EntityManager } from "typeorm"
class MyImportStrategy extends AbstractBatchJobStrategy {
protected batchJobService_: BatchJobService
protected manager_: EntityManager
protected transactionManager_: EntityManager
processJob(batchJobId: string): Promise<void> {
throw new Error("Method not implemented.")
}
buildTemplate(): Promise<string> {
throw new Error("Method not implemented.")
}
}
export default MyImportStrategy
```
:::note
This is the base implementation of a batch job strategy. You can learn about all the different methods and properties in [this documentation](./create.mdx#3-define-required-properties).
:::
### 3. Set the batchType Property
Every batch job strategy class must have the static property `batchType` defined. It determines the type of batch job this strategy handles.
Since only one batch job strategy can handle a batch job type, you can overwrite Medusas default batch job strategies by using the same `batchType` value in your custom strategy.
So, for example, to overwrite the product import strategy set the `batchType` property in your strategy to `product-import`:
```ts
class MyImportStrategy extends AbstractBatchJobStrategy {
static batchType = "product-import"
// ...
}
```
### 4. Define your Custom Functionality
You can now define your custom functionality in your batch job strategy. For example, you can create custom import logic to import products.
Refer to the [Create a Batch Job documentation](./create.mdx#3-define-required-properties) to understand what properties and methods are required in your batch job strategy and how you can use them to implement your custom functionality.
### 5. Run Build Command
Before you can test out your batch job strategy, you must run the `build` command:
```bash npm2yarn
npm run build
```
### 6. Test your Functionality
Since you didnt create a new batch job type and overwrote the functionality of the strategy, you can test out your functionality using the [same steps used with the default strategy](./create.mdx#test-your-batch-job-strategy).
Specifically, since you create batch jobs using the [Create Batch Job](/api/admin/#tag/Batch-Job/operation/PostBatchJobs) endpoint which accepts the batch job type as a body parameter, you just need to send the same type you used for this field. In the example of this documentation, the `type` would be `product-import`.
If you overwrote the import functionality, you can follow [these steps to learn how to import products using the Admin APIs](../../modules/products/admin/import-products.mdx).
---
## Create Custom Batch Job Strategy
If you dont want to overwrite Medusas batch job strategy, you can create a custom batch job strategy with a different `batchType` value. Then, use that type when you send a request to [Create a Batch Job](/api/admin/#tag/Batch-Job).
For more details on creating custom batch job strategies, please check out the [Create Batch Job Strategy documentation](./create.mdx).
---
## See Also
- [Use the Import Product APIs](../../modules/products/admin/import-products.mdx).

View File

@@ -0,0 +1,95 @@
---
description: 'Learn what batch jobs in Medusa are and their flow. Batch jobs are tasks that can be performed asynchronously and iteratively in Medusa.'
---
import DocCardList from '@theme/DocCardList';
import Icons from '@theme/Icon';
# Batch Jobs
In this document, youll learn what Batch Jobs are and how they work in Medusa.
## What are Batch Jobs
Batch Jobs are tasks that can be performed asynchronously and iteratively. They can be [created using the Admin API](/api/admin/#tag/Batch-Job/operation/PostBatchJobs), then, once confirmed, they are processed asynchronously.
Batch jobs require Redis, which Medusa uses as a queuing system to register and handle events. Every status change of a batch job triggers an event that can be handled using [subscribers](../events/subscribers.mdx).
Medusa uses batch jobs in its core to perform some asynchronous tasks. For example, the Export Products functionality uses batch jobs.
You can also create a custom batch job or overwrite Medusas batch jobs.
### BatchJob Entity Overview
A batch job is stored in the database as a [BatchJob](../../references/entities/classes/BatchJob) entity. Some of its important attributes are:
- `status`: A string that determines the current status of the Batch Job.
- `context`: An object that can be used to store configurations related to the batch job. For example, you can use it to store listing configurations such as the limit or offset of the list to be retrieved during the processing of the batch job.
- `dry_run`: A boolean value that indicates whether this batch job should actually produce an output. By default its false, meaning that by default it produces an output. It can be used to simulate a batch job.
- `type`: A string that is used to later resolve the batch job strategy that should be used to handle the batch job.
- `result`: An object that includes important properties related to the result of the batch job. Some of these properties are:
- `errors`: An error object that contains any errors that occur during the batch jobs execution.
- `progress`: A numeric value indicating the progress of the batch job.
- `count`: A number that includes the total count of records related to the operation. For example, in the case of product exports, it is used to indicate the total number of products exported.
- `advancement_count`: A number that indicates the number of records processed so far. Can be helpful when retrying a batch job.
---
## What are Batch Job Strategies
Batch jobs are handled by batch job strategies. A batch job strategy is a class that extends the `AbstractBatchJobStrategy` abstract class and implements the methods defined in that class to handle the different states of a batch job.
A batch job strategy must implement the necessary methods to handle the preparation of a batch job before it is created, the preparation of the processing of the batch job after it is created, and the processing of the batch job once it is confirmed.
When you create a batch job strategy, the `batchType` class property indicates the batch job types this strategy handles. Then, when you create a new batch job, you set the batch jobs type to the value of `batchType` in your strategy.
---
## How Batch Jobs Work
A batch jobs flow from creation to completion is:
1. A batch job is created using the [Create Batch Job API endpoint](/api/admin/#tag/Batch-Job/operation/PostBatchJobs).
2. Once the batch job is created, the batch jobs status is changed to `created` and the `batch.created` event is triggered by the `BatchJobService`.
3. The `BatchJobSubscriber` handles the `created` event. It resolves the batch job strategy based on the `type` of the batch job, then uses it to pre-process the batch job. After this, the batch jobs status is changed to `pre_processed`. Only when the batch job has the status `pre_processed` can be confirmed.
4. If `dry_run` is not set in the Create Batch Job request in step one or if it is set to `false`, the batch job will automatically be confirmed after processing. Otherwise, if `dry_run` is set to `true`, the batch job can be confirmed using the [Confirm Batch Job API](/api/admin/#tag/Batch-Job/operation/PostBatchJobsBatchJobConfirmProcessing) endpoint.
5. Once the batch job is confirmed, the batch jobs status is changed to `confirmed` and the `batch.confirmed` event is triggered by the `BatchJobService`.
6. The `BatchJobSubscriber` handles the `confirmed` event. It resolves the batch job strategy, then uses it to process the batch job.
7. Once the batch job is processed succesfully, the batch job has the status `completed`.
You can track the progress of the batch job at any point using the [Retrieve Batch Job](/api/admin/#tag/Batch-Job/operation/GetBatchJobsBatchJob) endpoint.
:::info
If the batch job fails at any point in this flow, its status is changed to `failed`.
:::
![Flowchart summarizing the batch job's flow from creation to completion](https://res.cloudinary.com/dza7lstvk/image/upload/v1668001632/Medusa%20Docs/Diagrams/Qja0kAz_ns4vm8.png)
---
## Custom Development
Developers can create custom batch jobs in the Medusa backend, a plugin, or in a Commerce Module. Developers can also customize existing batch job strategies in the core, such as the import strategy.
<DocCardList colSize={6} items={[
{
type: 'link',
href: '/development/batch-jobs/create',
label: 'Create a Batch Job Strategy',
customProps: {
icon: Icons['academic-cap-solid'],
description: 'Learn how to create a batch job strategy in Medusa.'
}
},
{
type: 'link',
href: '/development/batch-jobs/customize-import',
label: 'Customize Import Strategy',
customProps: {
icon: Icons['academic-cap-solid'],
description: 'Learn how to customize the import strategy in Medusa.'
}
},
]} />

View File

@@ -0,0 +1,76 @@
---
description: 'Learn how to add a middleware in Medusa. A middleware is a function that has access to the request and response objects and can be used to perform actions around an endpoint.'
addHowToData: true
---
# How to Add a Middleware
In this document, youll learn how to add a middleware to an existing or custom route in Medusa.
## Overview
As the Medusa backend is built on top of [Express](https://expressjs.com/), Expresss features can be utilized during your development with Medusa.
One feature in particular is adding a [middleware](http://expressjs.com/en/guide/using-middleware.html#using-middleware). A middleware is a function that has access to the request and response objects.
A middleware can be used to perform an action when an endpoint is called or modify the response, among other usages.
You can add a middleware to an existing route in the Medusa backend, a route in a plugin, or your custom routes.
---
## Add a Middleware
Adding a middleware is very similar to adding a custom endpoint. The middleware must be created either in the `src/api/index.ts` entry point, or other TypeScript or JavaScript files imported into `src/api/index.ts`.
:::info
Learn more about creating custom endpoints in [this documentation](./create.md).
:::
The following code snippet is an example of adding a middleware:
```ts title=src/api/index.ts
import { Router } from "express"
export default () => {
const router = Router()
router.use("/store/products", (req, res, next) => {
// perform an action when retrieving products
next()
})
return router
}
```
This code snippet adds a middleware to the [List Products](/api/store/#tag/Product/operation/GetProducts) endpoint. In the middleware function, you can perform any action.
Then, you must call the `next` method received as a third parameter in the middleware function to ensure that the endpoint executes after the middleware.
:::info
You can learn more about Middlewares and their capabilities in [Expresss documentation](http://expressjs.com/en/guide/using-middleware.html#using-middleware).
:::
---
## Building Files
Similar to custom endpoints, you must transpile the files under `src` into the `dist` directory for the backend to load them.
To do that, run the following command before running the Medusa backend:
```bash npm2yarn
npm run build
```
---
## See Also
- [Store API reference](/api/store)
- [Admin API reference](/api/admin)

View File

@@ -0,0 +1,339 @@
---
description: 'Learn how to create endpoints in Medusa. This guide also includes how to add CORS configurations, creating multiple endpoints, adding protected routes, and more.'
addHowToData: true
---
# How to Create Endpoints
In this document, youll learn how to create endpoints in Medusa.
## Overview
Custom endpoints are created under the `src/api` directory in your Medusa Backend. They're defined in a TypeScript or JavaScript file named `index` (for example, `index.ts`). This file should export a function that returns an Express router
They're then transpiled into the `/dist/api` directory to be consumed.
---
## Implementation
To create a new endpoint, start by creating a new file in `src/api` called `index.ts`. At its basic format, `index.ts` should look something like this:
```ts title=src/api/index.ts
import { Router } from "express"
export default (rootDirectory, pluginOptions) => {
const router = Router()
router.get("/hello", (req, res) => {
res.json({
message: "Welcome to My Store!",
})
})
return router
}
```
This exports a function that returns an Express router. The function receives two parameters:
- `rootDirectory` is the absolute path to the root directory that your backend is running from.
- `pluginOptions` is an object that has your plugin's options. If your API route is not implemented in a plugin, then it will be an empty object.
### Endpoints Path
Your endpoint can be under any path you wish.
By Medusas conventions:
- All Storefront REST APIs are prefixed by `/store`. For example, the `/store/products` endpoint lets you retrieve the products to display them on your storefront.
- All Admin REST APIs are prefixed by `/admin`. For example, the `/admin/products` endpoint lets you retrieve the products to display them on your Admin.
You can also create endpoints that don't reside under these two prefixes, similar to the `hello` endpoint in the previous example.
---
## CORS Configuration
If youre adding a storefront or admin endpoint and you want to access these endpoints from the storefront or Medusa admin, you need to pass your endpoints Cross-Origin Resource Origin (CORS) options using the `cors` package.
First, you need to import the necessary utility functions and types from Medusa's packages with the `cors` library:
```ts
import {
getConfigFile,
parseCorsOrigins,
} from "medusa-core-utils"
import {
ConfigModule,
} from "@medusajs/medusa/dist/types/global"
import cors from "cors"
```
Next, in the exported function, retrieve the CORS configurations of your backend using the utility functions you imported:
```ts
export default (rootDirectory) => {
// ...
const { configModule } =
getConfigFile<ConfigModule>(rootDirectory, "medusa-config")
const { projectConfig } = configModule
// ....
}
```
Then, create an object that will hold the CORS configurations. If its a storefront endpoint, pass the `origin` property storefront options:
```ts
const corsOptions = {
origin: projectConfig.store_cors.split(","),
credentials: true,
}
```
If its an admin endpoint, pass the `origin` property admin options:
```ts
const corsOptions = {
origin: projectConfig.admin_cors.split(","),
credentials: true,
}
```
Finally, for each route you add, create an `OPTIONS` request and add `cors` as a middleware for the route passing it the CORS option:
```ts
router.options("/admin/hello", cors(corsOptions))
router.get("/admin/hello", cors(corsOptions), (req, res) => {
// ...
})
```
---
## Create Multiple Endpoints
### Same File
You can add more than one endpoint in `src/api/index.ts`:
```ts title=src/api/index.ts
router.options("/store/hello", cors(storeCorsOptions))
router.get(
"/store/hello",
cors(storeCorsOptions),
(req, res) => {
res.json({
message: "Welcome to Your Store!",
})
}
)
router.options("/admin/hello", cors(adminCorsOptions))
router.get(
"/admin/hello",
cors(adminCorsOptions),
(req, res) => {
res.json({
message: "Welcome to Your Admin!",
})
}
)
```
### Multiple Files
Alternatively, you can add multiple files for each endpoint or set of endpoints for readability and easy maintenance.
To do that with the previous example, first, create the file `src/api/store.ts` with the following content:
```ts title=src/api/store.ts
import cors from "cors"
import { projectConfig } from "../../medusa-config"
export default (router) => {
const storeCorsOptions = {
origin: projectConfig.store_cors.split(","),
credentials: true,
}
router.options("/store/hello", cors(storeCorsOptions))
router.get(
"/store/hello",
cors(storeCorsOptions),
(req, res) => {
res.json({
message: "Welcome to Your Store!",
})
}
)
}
```
You export a function that receives an Express router as a parameter and adds the endpoint `store/hello` to it.
Next, create the file `src/api/admin.ts` with the following content:
```ts title=src/api/admin.ts
import cors from "cors"
import { projectConfig } from "../../medusa-config"
export default (router) => {
const adminCorsOptions = {
origin: projectConfig.admin_cors.split(","),
credentials: true,
}
router.options("/admin/hello", cors(adminCorsOptions))
router.get(
"/admin/hello",
cors(adminCorsOptions),
(req, res) => {
res.json({
message: "Welcome to Your Admin!",
})
}
)
}
```
Again, you export a function that receives an Express router as a parameter and adds the endpoint `admin/hello` to it.
Finally, in `src/api/index.ts` import the two functions at the beginning of the file:
```ts title=src/api/index.ts
import { Router } from "express"
import storeRoutes from "./store"
import adminRoutes from "./admin"
```
and in the exported function, call each of the functions passing them the Express router:
```ts title=src/api/index.ts
export default () => {
const router = Router()
storeRoutes(router)
adminRoutes(router)
return router
}
```
---
## Protected Routes
Protected routes are routes that should be accessible by logged-in customers or users only.
### Protect Store Routes
To make a storefront route protected, first, import the `authenticate-customer` middleware:
<!-- eslint-disable max-len -->
```ts
import
authenticate
from "@medusajs/medusa/dist/api/middlewares/authenticate-customer"
```
Then, add the middleware to your route:
```ts
router.options("/store/hello", cors(corsOptions))
router.get("/store/hello", cors(corsOptions), authenticate(),
async (req, res) => {
if (req.user) {
// user is logged in
// to get customer id: req.user.customer_id
}
// ...
}
)
```
Please note that the endpoint is still accessible by all users, however, youll be able to access the current logged-in customer if theres any.
To disallow guest customers from accessing the endpoint, you can throw an error if `req.user` is `false`.
### Protect Admin Routes
To make an admin route protected, first, import the `authenticate` middleware:
<!-- eslint-disable max-len -->
```ts
import
authenticate
from "@medusajs/medusa/dist/api/middlewares/authenticate"
```
Then, add the middleware to your route:
```ts
router.options("/admin/products/count", cors(corsOptions))
router.get(
"/admin/products/count",
cors(corsOptions),
authenticate(),
async (req, res) => {
// access current user
const id = req.user.userId
const userService = req.scope.resolve("userService")
const user = await userService.retrieve(id)
// ...
}
)
```
Now, only authenticated users can access this endpoint.
---
## Use Services
Services in Medusa bundle a set of functionalities into one class. Then, you can use that class anywhere in your backend. For example, you can use the `ProductService` to retrieve products or perform operations like creating or updating a product.
You can retrieve any registered service in your endpoint using `req.scope.resolve` passing it the services registration name.
Heres an example of an endpoint that retrieves the count of products in your store:
```ts
router.get(
"/admin/products/count",
cors(corsOptions),
authenticate(),
(req, res) => {
const productService = req.scope.resolve("productService")
productService.count().then((count) => {
res.json({
count,
})
})
}
)
```
The `productService` has a `count` method that returns a Promise. This Promise resolves to the count of the products. You return a JSON of the product count.
---
## Building Files
Custom endpoints must be transpiled and moved to the `dist` directory before you can start consuming them. When you run your backend using the `medusa develop` command, it watches the files under `src` for any changes, then triggers the `build` command and restarts the server.
The build isn't triggerd though when the backend first starts running. So, make sure to run the `build` command before starting the backend:
```bash npm2yarn
npm run build
```
---
## See Also
- [Storefront API Reference](/api/store)
- [Admin API Reference](/api/admin)

View File

@@ -0,0 +1,76 @@
---
description: "Learn what endpoints are in Medusa. Endpoints are REST APIs that allow a frontend or external system to interact with the Backend."
---
import DocCardList from '@theme/DocCardList';
import Icons from '@theme/Icon';
# Endpoints
In this document, youll learn what endpoints are in Medusa.
## Introduction
The Medusa Backend is a web server built on top of [Express](https://expressjs.com/), a Node.js web framework. This provides developers with all the functionalities available within Express during development. One of those are endpoints.
Endpoints are REST APIs that allow a frontend or an external system to interact with the Medusa Backend to retrieve and process data, or perform business logic. Endpoints are [Express routes](https://expressjs.com/en/starter/basic-routing.html).
Each [Commerce Module](../../modules/overview.mdx) contains a set of endpoints specific to the functionalities that it provides. Since the core package that powers the Medusa Backend acts as an orchestrator of Commerce Modules and exposes their endpoints, the endpoints of each of these Commerce Modules are available within the Medusa Backend.
The Commerce Modules provide two types of endpoints: Store APIs and Admin APIs. The Store APIs are typically accessed from the storefront. For example, you can use the Store APIs to show customers available products or implement a cart and checkout flow.
The Admin APIs are typically accessed from an admin dashboard. For example, you can use the Admin APIs to allow admins to manage the stores data such as products, orders, and so on.
<DocCardList colSize={6} items={[
{
type: 'link',
href: '/api/store',
label: 'Store APIs',
customProps: {
icon: Icons['server-solid'],
description: 'Check out available Store REST APIs.'
}
},
{
type: 'link',
href: '/api/admin',
label: 'Admin APIs',
customProps: {
icon: Icons['server-solid'],
description: 'Check out available Admin REST APIs.'
}
},
]} />
---
## Custom Development
Aside from using the endpoints that Commerce Modules, developers can create their own REST APIs either directly in the Medusa Backend, in a plugin, or in a custom Commerce Module.
:::tip
As the core Medusa package is completely customizable, developers can also extend the functionality even further to implement GraphQL endpoints.
:::
<DocCardList colSize={6} items={[
{
type: 'link',
href: '/development/endpoints/create',
label: 'Create an Endpoint',
customProps: {
icon: Icons['academic-cap-solid'],
description: 'Learn how to create an endpoint in Medusa.'
}
},
{
type: 'link',
href: '/development/endpoints/add-middleware',
label: 'Add a Middleware',
customProps: {
icon: Icons['academic-cap-solid'],
description: 'Learn how to add a middleware in Medusa.'
}
},
]} />

View File

@@ -0,0 +1,146 @@
---
description: 'Learn how to create an entity in Medusa. This guide also explains how to create a repository and access and delete the entity.'
addHowToData: true
---
# Create an Entity
In this document, youll learn how you can create an [Entity](./overview.mdx).
## Create the Entity
To create an entity, create a TypeScript file in `src/models`. For example, heres a `Post` entity defined in the file `src/models/post.ts`:
```ts title=src/models/post.ts
import {
BeforeInsert,
Column,
Entity,
PrimaryColumn,
} from "typeorm"
import { BaseEntity } from "@medusajs/medusa"
import { generateEntityId } from "@medusajs/medusa/dist/utils"
@Entity()
export class Post extends BaseEntity {
@Column({ type: "varchar" })
title: string | null
@BeforeInsert()
private beforeInsert(): void {
this.id = generateEntityId(this.id, "post")
}
}
```
This entity has one column `title` defined. However, since it extends `BaseEntity` it will also have the `id`, `created_at`, and `updated_at` columns.
Medusas core entities all have the following format for IDs: `<PREFIX>_<RANDOM>`. For example, an order might have the ID `order_01G35WVGY4D1JCA4TPGVXPGCQM`.
To generate an ID for your entity that matches the IDs generated for Medusas core entities, you should add a `BeforeInsert` event handler. Then, inside that handler use Medusas utility function `generateEntityId` to generate the ID. It accepts the ID as a first parameter and the prefix as a second parameter. The `Post` entity IDs will be of the format `post_<RANDOM>`.
If you want the entity to also be soft deletable then it should extend `SoftDeletableEntity` instead:
```ts
import { SoftDeletableEntity } from "@medusajs/medusa"
@Entity()
export class Post extends SoftDeletableEntity {
// ...
}
```
You can learn more about what decorators and column types you can use in [Typeorms documentation](https://typeorm.io/entities).
### Create a Migration
Additionally, you must create a migration for your entity. Migrations are used to update the database schema with new tables or changes to existing tables.
You can learn more about Migrations, how to create them, and how to run them in the [Migration documentation](./migrations/overview.mdx).
### Create a Repository
Entities data can be easily accessed and modified using Typeorm [Repositories](https://typeorm.io/working-with-repository). To create a repository, create a file in `src/repositories`. For example, heres a repository `PostRepository` created in `src/repositories/post.ts`:
```ts title=src/repositories/post.ts
import { EntityRepository, Repository } from "typeorm"
import { Post } from "../models/post"
@EntityRepository(Post)
export class PostRepository extends Repository<Post> { }
```
This repository is created for the `Post` and that is indicated using the decorator `@EntityRepository`.
:::tip
Be careful with your file names as it can cause unclear errors in Typeorm. Make sure all your file names are small letters for both entities and repositories to avoid any issues with file names.
:::
---
## Access a Custom Entity
:::note
Before trying this step make sure that youve created and run your migrations. You also need to re-build your code using:
```bash npm2yarn
npm run build
```
:::
You can access your custom entity data in the database in services or subscribers using the repository. For example, heres a service that lists all posts:
```ts
import { TransactionBaseService } from "@medusajs/medusa"
class PostService extends TransactionBaseService {
constructor({ postRepository, manager }) {
super({ postRepository, manager })
this.postRepository = postRepository
this.manager_ = manager
}
async list() {
const postRepository = this.manager_
.getCustomRepository(this.postRepository)
return await postRepository.find()
}
}
export default PostService
```
In the constructor, you can use dependency injection to get access to instances of services and repositories. Here, you initialize class fields `postRepository` and `manager`. The `manager` is a [Typeorm Entity Manager](https://typeorm.io/working-with-entity-manager).
Then, in the method `list`, you can obtain an instance of the `PostRepository` using `this.manager_.getCustomRepository` passing it `this.postRepository` as a parameter. This lets you use [Custom Repositories with Typeorm](https://typeorm.io/custom-repository) to create custom methods in your repository that work with the data in your database.
After getting an instance of the repository, you can then use [Typeorms Repository methods](https://typeorm.io/repository-api) to perform Create, Read, Update, and Delete (CRUD) operations on your entity.
If you need access to your entity in endpoints, you can then use the methods you define in the service.
:::note
This same usage of repositories can be done in subscribers as well.
:::
### Delete a Soft-Deletable Entity
To delete soft-deletable entities that extend the `SoftDeletableEntity` class, you can use the repository method `softDelete` method:
```ts
await postRepository.softDelete(post.id)
```
---
## See Also
- [Migrations Overview](./migrations/overview.mdx)
- [Create a Plugin](../plugins/create.md)

View File

@@ -0,0 +1,109 @@
---
description: 'Learn how to create a migration in Medusa. This guide explains how to write and run migrations.'
addHowToData: true
---
# How to Create Migrations
In this document, youll learn how to create a [Migration](./overview.mdx) using [Typeorm](https://typeorm.io) in Medusa.
## Step 1: Create Migration File
To create a migration that makes changes to your Medusa schema, run the following command:
```bash
npx typeorm migration:create -n UserChanged --dir src/migrations
```
This will create the migration file in the path you specify. You can use this without the need to install Typeorm's CLI tool. You can then go ahead and make changes to it as necessary.
The migration file must be inside the `src/migrations` directory. When you run the build command, it will be transpiled into the directory `dist/migrations`. The `migrations run` command can only pick up migrations under the `dist/migrations` directory on a Medusa backend. This applies to migrations created in a Medusa backend, and not in a Medusa plugin. For plugins, check out the [Plugin's Structure section](../../plugins/create.md).
<details>
<summary>Generating Migrations for Entities</summary>
You can alternatively use Typeorm's `generate` command to generate a Migration file from existing entity classes. As Medusa uses v0.2.45 of Typeorm, you have to create a `ormconfig.json` first before using the `generate` command.
:::note
Typeorm will be updated to the latest version in v1.8.0 of Medusa.
:::
For example, create the file `ormconfig.json` in the root of your Medusa server with the following content:
```json
{
"type": "postgres",
"host": "localhost",
"port": 5432,
"username": "<YOUR_DB_USERNAME>",
"password": "<YOUR_DB_PASSWORD>",
"database": "<YOUR_DB_NAME>",
"synchronize": true,
"logging": false,
"entities": [
"dist/models/**/*.js"
],
"migrations": [
"dist/migrations/**/*.js"
],
"cli": {
"entitiesDir": "src/models",
"migrationsDir": "src/migrations"
}
}
```
Make sure to replace `<YOUR_DB_USERNAME>`, `<YOUR_DB_PASSWORD>`, and `<YOUR_DB_NAME>` with the necessary values for your database connection.
Then, after creating your entity, run the `build` command:
```bash npm2yarn
npm run build
```
Finally, run the following command to generate a Migration for your new entity:
```bash
npx typeorm@0.2.45 migration:generate -n PostCreate
```
Where `PostCreate` is just an example of the name of the migration to generate. The migration will then be generated in `src/migrations/<TIMESTAMP>-PostCreate.ts`. You can then skip to step 3 of this guide.
</details>
---
## Step 2: Write Migration File
The migration file contains the necessary commands to create the database columns, foreign keys, and more.
You can learn more about writing the migration file in You can learn more about writing migrations in [Typeorms Documentation](https://typeorm.io/migrations).
---
## Step 3: Build Files
Before you can run the migrations you need to run the build command to transpile the TypeScript files to JavaScript files:
```bash npm2yarn
npm run build
```
---
## Step 4: Run Migration
The last step is to run the migration with the command detailed earlier
```bash
medusa migrations run
```
If you check your database now you should see that the change defined by the migration has been applied successfully.
---
## See Also
- [Create a Plugin](../../plugins/create.md)

View File

@@ -0,0 +1,85 @@
---
description: 'Learn what Migrations are in Medusa and how to run them. Migrations are used to make changes to the database schema that Medusa is linked to.'
---
import DocCardList from '@theme/DocCardList';
import Icons from '@theme/Icon';
# Migrations
In this document, you'll learn what Migrations are in Medusa.
:::note
Medusas Migrations do not work with SQLite databases. They are intended to be used with PostgreSQL databases, which is the recommended database for using Medusa in production.
:::
## What are Migrations
Migrations are scripts that are used to make additions or changes to your database schema. In Medusa, they are essential for both when you first install your backend and for subsequent backend upgrades later on.
When you first create your Medusa backend, the database schema used must have all the tables necessary for the backend to run.
When a new Medusa version introduces changes to the database schema, you'll have to run migrations to apply them to your own database.
:::tip
Migrations are used to apply changes to the database schema. However, there are some version updates of Medusa that require updating the data in your database to fit the new schema. Those are specific to each version and you should check out the version under Upgrade Guides for details on the steps.
:::
---
## How to Run Migrations
Migrations in Medusa can be done in one of two ways:
### Migrate Command
Using the Medusa CLI tool, you can run migrations with the following command:
```bash
medusa migrations run
```
This will check for any migrations that contain changes to your database schema that aren't applied yet and run them on your backend.
### Seed Command
Seeding is the process of filling your database with data that is either essential or for testing and demo purposes. In Medusa, the `seed` command will run the migrations to your database if necessary before it seeds your database with dummy data.
You can use the following command to seed your database:
```bash npm2yarn
npm run seed
```
This will use the underlying `seed` command provided by Medusa's CLI to seed your database with data from the file `data/seed.json` on your Medusa backend.
---
## Custom Development
Developers can create custom entities in the Medusa backend, a plugin, or in a Commerce Module, then ensure it reflects in the database using a migration.
<DocCardList colSize={6} items={[
{
type: 'link',
href: '/development/entities/migrations/create',
label: 'Create a Migration',
customProps: {
icon: Icons['academic-cap-solid'],
description: 'Learn how to create migrations in Medusa.'
}
},
{
type: 'link',
href: '/development/entities/create',
label: 'Create an Endpoint',
customProps: {
icon: Icons['academic-cap-solid'],
description: 'Learn how to create endpoints in Medusa.'
}
},
]} />

View File

@@ -0,0 +1,94 @@
---
description: 'Learn what entities are in Medusa. There are entities defined in the Medusa backend, and developers can create custom entities.'
---
import DocCardList from '@theme/DocCardList';
import Icons from '@theme/Icon';
# Entities
In this document, you'll learn what Entities are in Medusa.
## What are Entities
Entities in medusa represent tables in the database as classes. An example of this would be the `Order` entity which represents the `order` table in the database. Entities provide a uniform way of defining and interacting with data retrieved from the database.
Aside from the entities in the Medusa core package, you can also create your own entities to use in your Medusa backend. Custom entities are TypeScript or JavaScript files located in the `src/models` directory of your Medusa backend. These entities are then transpiled to the `dist/models` directory to be used during the backend's runtime.
Entities are TypeScript files and they are based on [Typeorms Entities](https://typeorm.io/entities) and use Typeorm decorators.
---
## Base Entities
All entities must extend either the `BaseEntity` or `SoftDeletableEntity` classes. The `BaseEntity` class holds common columns including the `id`, `created_at`, and `updated_at` columns.
The `SoftDeletableEntity` class extends the `BaseEntity` class and adds another column `deleted_at`. If an entity can be soft deleted, meaning that a row in it can appear to the user as deleted but still be available in the database, it should extend `SoftDeletableEntity`.
---
## metadata Attribute
Most entities in Medusa have a `metadata` attribute. This attribute is an object that can be used to store custom data related to that entity. In the database, this attribute is stored as a [JSON Binary (JSONB)](https://www.postgresql.org/docs/current/datatype-json.html#JSON-CONTAINMENT) column. On retrieval, the attribute is parsed into an object.
Some example use cases for the `metadata` attribute include:
- Store an external ID of an entity related to a third-party integartion.
- Store product customization such as personalization options.
### Add and Update Metadata
You can add or update metadata entities either through the REST APIs or through create and update methods in the entity's respective service.
In the [admin REST APIs](/api/admin), you'll find that in create or update requests of some entities you can also set the `metadata`.
In services, there are typically `create` or `update` methods that allow you to set or update the metadata.
If you want to add a property to the `metadata` object or update a property in the `metadata` object, you can pass the `metadata` object with the properties you want to add or update in it. For example:
```json
{
// other data
"metadata": {
"is_b2b": true
}
}
```
If you want to remove a property from the `metadata` object, you can pass the `metadata` object with the property you want to delete. The property should have an empty string value. For example:
```json
{
// other data
"metadata": {
"is_b2b": "" // this deletes the `is_b2b` property from `metadata`
}
}
```
---
## Custom Development
Developers can create custom entities in the Medusa backend, a plugin, or in a Commerce Module, then ensure it reflects in the database using a migration.
<DocCardList colSize={6} items={[
{
type: 'link',
href: '/development/entities/create',
label: 'Create an Endpoint',
customProps: {
icon: Icons['academic-cap-solid'],
description: 'Learn how to create endpoints in Medusa.'
}
},
{
type: 'link',
href: '/development/entities/migrations/create',
label: 'Create a Migration',
customProps: {
icon: Icons['academic-cap-solid'],
description: 'Learn how to create migrations in Medusa.'
}
},
]} />

View File

@@ -0,0 +1,94 @@
---
description: 'Learn how to create a subscriber in Medusa. You can use subscribers to implement functionalities like sending an order confirmation email.'
addHowToData: true
---
# How to Create a Subscriber
In this document, youll learn how to create a [Subscriber](./subscribers.mdx) in Medusa that listens to events to perform an action.
## Prerequisites
Medusa's event system works by pushing data to a Queue that each handler then gets notified of. The queuing system is based on Redis, so it's required for subscribers to work.
You can learn how to [install Redis](../backend/prepare-environment.mdx#redis) and [configure it with Medusa](../backend/configurations.md#redis) before you get started.
---
## Implementation
A subscriber is a TypeScript or JavaScript file that is created under `src/subscribers`. Its file name, by convension, should be the class name of the subscriber without the word `Subscriber`. For example, if the subscriber is `HelloSubscriber`, the file name should be `hello.ts`.
After creating the file under `src/subscribers`, in the constructor of your subscriber, listen to events using `eventBusService.subscribe` , where `eventBusService` is a service injected into your subscribers constructor.
The `eventBusService.subscribe` method receives the name of the event as a first parameter and as a second parameter a method in your subscriber that will handle this event.
For example, here is the `OrderNotifierSubscriber` class created in `src/subscribers/orderNotifier.ts`:
```ts title=src/subscribers/orderNotifier.ts
class OrderNotifierSubscriber {
constructor({ eventBusService }) {
eventBusService.subscribe("order.placed", this.handleOrder)
}
handleOrder = async (data) => {
console.log("New Order: " + data.id)
}
}
export default OrderNotifierSubscriber
```
This subscriber registers the method `handleOrder` as one of the handlers of the `order.placed` event. The method `handleOrder` will be executed every time an order is placed. It receives the order ID in the `data` parameter. You can then use the orders details to perform any kind of task you need.
:::note
The `data` object won't contain other order data. Only the ID of the order. You can retrieve the order information using the `orderService`.
:::
---
## Using Services in Subscribers
You can access any service through the dependencies injected to your subscribers constructor.
For example:
```ts
class OrderNotifierSubscriber {
constructor({ productService, eventBusService }) {
this.productService = productService
eventBusService.subscribe(
"order.placed",
this.handleOrder
)
}
// ...
}
```
You can then use `this.productService` anywhere in your subscribers methods. For example:
```ts
class OrderNotifierSubscriber {
// ...
handleOrder = async (data) => {
// ...
const product = this.productService.list()
}
}
```
:::note
When using attributes defined in the subscriber, such as the `productService` in the example above, you must use an arrow function to declare the method. Otherwise, the attribute will be undefined when used.
:::
---
## See Also
- [Create a Plugin](../plugins/create.md)

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,227 @@
---
description: 'Learn how the events system is implemented in Medusa. It is built on a publish-subscribe architecture. The Medusa core publishes events when certain actions take place.'
---
# Events Architecture
In this document, you'll learn how the events system is implemented in Medusa.
## Overview
The events system in Medusa is built on a publish/subscribe architecture. The Medusa core publishes events when certain actions take place.
Those events can be subscribed to using subscribers. When you subscribe to an event, you can perform a task asynchronusly every time the event is triggered.
:::info
You can learn more about subscribers and their use cases in the [Subscribers](./subscribers.mdx) documentation.
:::
---
## Publishing and Subscribing
The `EventBusService` is responsible for publishing and processing events.
:::note
The current implementation of the `EventBusService` is powered by Redis. However, an upcoming version of Medusa introduces an event bus module. This will allow you to use any publishing and subscribing provider. That will not change the general purpose and flow of the `EventBusService`.
:::
The `EventBusService` exposes two methods in its public API for event processing; `emit` and `subscribe`.
### emit
The `emit` method accepts as a first parameter the event name. It adds it to a Bull queue (powered by Redis) as a job, and processes it asynchronously.
The second parameter contains any data that should be emitted with the event. Subscribers that handle the event will receive that data as a method parameter.
The third parameter is an options object. It accepts options related to the number of retries if a subscriber handling the event fails, the delay time, and more. The options are explained in a [later section](#retrying-handlers)
The `emit` method has the following signature:
```ts
export default class EventBusService {
// ...
async emit<T>(
eventName: string,
data: T,
options: Record<string, unknown> &
EmitOptions = { attempts: 1 }
): Promise<StagedJob | void>
}
```
Here's an example of how you can emit an event using the `EventBusService`:
```ts
eventBusService.emit(
"product.created",
{ id: "prod_..." },
{ attempts: 2 }
)
```
The `EventBusService` emits the event `product.created` by passing the event name as a first argument. An object is passed as a second argument which is the data passed to the event handler methods in subscribers. This object contains the ID of the product.
Options are passed in the third argument. The `attempt` property specifies how many times the subscriber should be retried if it fails (by default it's one).
### subscribe
The `subscribe` method will attach a handler method to the specified event, which is run when the event is triggered. It is usually used insde a subscriber class.
The `subscribe` method accepts the event name as the first parameter. This is the event that the handler method will attach to.
The second parameter is the handler method that will be triggered when the event is emitted.
The third parameter is an optional `context` parameter. It allows you to configure the ID of the handler method.
The `subscribe` method has the following signature:
```ts
export default class EventBusService {
// ...
subscribe(
event: string | symbol,
subscriber: Subscriber,
context?: SubscriberContext
): this
}
```
Here's an example of how you can subscribe to an event using the `EventBusService`:
```ts title=src/subscribers/my.ts
import { EventBusService } from "@medusajs/medusa"
class MySubscriber {
constructor({
eventBusService: EventBusService,
}) {
eventBusService.subscribe("product.created", (data) => {
// TODO handle event
console.log(data.id)
})
}
}
```
In the constructor of a subscriber, you use the `EventBusService` to subscribe to the event `product.created`. In the handler method, you can perform a task every time the product is created. Notice how the handler method accepts the `data` as a parameter as explain in the previous section.
:::note
You can learn more about how to create a subscriber in [this documentation](./create-subscriber.md)
:::
---
## Processing Events
In the `EventBusService` service, the `worker_` method defines the logic run for each event emitted into the queue.
By default, all handler methods to that event are retrieved and, for each of the them, the stored data provided as a second parameter in `emit` is passed as an argument.
---
## Retrying Handlers
A handler method might fail to process an event. This could happen because it communicates with a third party service currently down or due to an error in its logic.
In some cases, you might want to retry those failed handlers.
As briefly explained earlier, you can pass options when emitting an event as a third argument that are used to configure how the queue worker processes your job. If you pass `attempts` upon emitting the event, the processing of a handler method is retried when it fails.
Aside from `attempts`, there are other options to futher configure the retry mechanism:
```ts
type EmitOptions = {
delay?: number
attempts: number
backoff?: {
type: "fixed" | "exponential"
delay: number
}
}
```
Here's what each of these options mean:
- `delay`: delay the triggering of the handler methods by a number of milliseconds.
- `attempts`: the number of times a subscriber handler should be retried when it fails.
- `backoff`: the wait time between each retry
### Note on Subscriber IDs
If you have more than one handler methods attached to a single event, or if you have multiple backend instances running, you must pass a subscriber ID as a third parameter to the `subscribe` method. This allows the `EventBusService` to differentiate between handler methods when retrying a failed one.
If a subscriber ID is not passed on subscription, all handler methods are run again. This can lead to data inconsistencies or general unwanted behavior in your system.
On the other hand, if you want all handler methods to run again when one of them fails, you can omit passing a subscriber ID.
An example of passing a subscriber ID:
```ts title=src/subscribers/my.ts
import { EventBusService } from "@medusajs/medusa"
class MySubscriber {
constructor({
eventBusService: EventBusService,
}) {
eventBusService.subscribe(
"product.created",
(data) => {
// TODO handle event
console.log(data.id)
},
"my-unique-subscriber")
}
}
```
:::info
You can learn more about subscriber IDs in [Bull's documentation](https://github.com/OptimalBits/bull/blob/develop/REFERENCE.md#queueadd).
:::
---
## Database transactions
<!-- vale docs.Acronyms = NO -->
Transactions in Medusa ensure atomicity, consistency, isolation, and durability, or ACID, guarantees for operations in the Medusa core.
<!-- vale docs.Acronyms = YES -->
In many cases, [services](../services/overview.mdx) typically update resources in the database and emit an event within a transactional operation. To ensure that these events don't cause data inconsistencies (for example, a plugin subscribes to an event to contact a third-party service, but the transaction fails) the concept of a staged job is introduced.
Instead of events being processed immediately, they're stored in the database as a staged job until they're ready. In other words, until the transaction has succeeded.
This rather complex logic is abstracted away from the consumers of the `EventBusService`, but here's an example of the flow when an API request is made:
1. API request starts.
2. Transaction is initiated.
3. Service layer performs some logic.
4. Events are emitted and stored in the database for eventual processing.
5. Transaction is committed.
6. API request ends.
7. Events in the database become visible.
To pull staged jobs from the database, a separate enqueuer polls the database every three seconds to discover new visible jobs. These jobs are then added to the queue and processed as described in the [Processing](#processing-events) section earlier.
:::info
This pattern is heavily inspired by the [Transactionally-staged Job Drain described in this blog post](https://brandur.org/job-drain).
:::
---
## See Also
- [Events reference](./events-list.md)
- [Create a subscriber](./create-subscriber.md)

View File

@@ -0,0 +1,55 @@
---
description: 'Learn what subscribers are in Medusa. Subscribers are used to listen to triggered events to perform an action.'
---
import DocCard from '@theme/DocCard';
import Icons from '@theme/Icon';
# Subscribers
In this document, you'll learn what Subscribers are in Medusa.
## What are Events
In Medusa, there are events that are emitted when a certain action occurs. For example, if a customer places an order, the `order.placed` event is emitted with the order data.
The purpose of these events is to allow other parts of the platform, or third-party integrations, to listen to those events and perform a certain action. That is done by creating subscribers.
Medusa's queuing and events system is handled by Redis. So, you must have [Redis configured](../backend/prepare-environment.mdx#redis) on your backend to use subscribers.
---
## What are Subscribers
Subscribers register handlers for an events and allows you to perform an action when that event occurs. For example, if you want to send your customer an email when they place an order, then you can listen to the `order.placed` event and send the email when the event is emitted.
Natively in Medusa there are subscribers to handle different events. However, you can also create your own custom subscribers.
Custom subscribers are TypeScript or JavaScript files in your project's `src/subscribers` directory. Files here should export classes, which will be treated as subscribers by Medusa. By convention, the class name should end with `Subscriber` and the file name should be the camel-case version of the class name without `Subscriber`. For example, the `WelcomeSubscriber` class is in the file `src/subscribers/welcome.ts`.
Whenever an event is emitted, the subscribers registered handler method is executed. The handler method receives as a parameter an object that holds data related to the event. For example, if an order is placed the `order.placed` event will be emitted and all the handlers will receive the order id in the parameter object.
### Example Use Cases
Subscribers are useful in many use cases, including:
- Send a confirmation email to the customer when they place an order by subscribing to the `order.placed` event.
- Automatically assign new customers to a customer group by subscribing to the `customer.created`.
- Handle custom events that you emit
---
## Custom Development
Developers can create custom subscribers in the Medusa backend, a plugin, or in a Commerce Module.
<DocCard item={{
type: 'link',
href: '/development/events/create-subscriber',
label: 'Create a Subscriber',
customProps: {
icon: Icons['academic-cap-solid'],
description: 'Learn how to create a subscriber in Medusa.'
}
}}
/>

View File

@@ -0,0 +1,28 @@
---
description: "Learn what feature flags in Medusa. Feature flags are used in Medusa to guard beta features that arent ready for live and production applications."
---
import DocCard from '@theme/DocCard';
import Icons from '@theme/Icon';
# Feature Flags
In this document, youll learn what feature flags in Medusa.
## Introduction
Feature flags are used in Medusa to guard beta features that arent ready for live and production applications. This allows the Medusa team to keep publishing releases more frequently, while also working on necessary future features behind the scenes. To use these beta features, you must enable their feature flags.
If a feature is guarded by a flag, entities, migrations, endpoints, and other resources associated with that feature are guarded by that flag as well. So, these resources will only be available to use in Medusa if you have enabled the associated feature flag.
You can view a list of available feature flags that you can toggle in [the Medusa GitHub mono-repository](https://github.com/medusajs/medusa/tree/master/packages/medusa/src/loaders/feature-flags). In each feature flag file, you can find the default value of the feature flag, its name, environment variable name, and more.
<DocCard item={{
type: 'link',
href: '/development/feature-flags/toggle',
label: 'Toggle Feature Flags',
customProps: {
icon: Icons['academic-cap-solid'],
description: 'Learn how to enable or disabled a feature flag.'
}
}} />

View File

@@ -0,0 +1,83 @@
---
description: 'Learn how to toggle feature flags in Medusa. This guide explains the steps required to toggle a feature flag.'
addHowToData: true
---
# How to Toggle Feature Flags
In this document, youll learn about how to toggle feature flags.
:::info
If a feature flag is enabled/disabled by default, you dont need to manually enable/disable it. Only set the feature flags value if its different than the default.
:::
## Enable Feature Flags
:::caution
Features guarded by feature flags are experimental and beta features. Enable them with caution.
:::
There are two ways to enable a feature flag.
### Method One: Using Environment Variables
You can enable a feature by setting the value of its environment variable to `true`. You can find [the name of a feature flags environment variable in the loader file](https://github.com/medusajs/medusa/tree/master/packages/medusa/src/loaders/feature-flags) its defined in. It is defined under the property `env_key` in the exported object.
For example, to enable the Tax-Inclusive Pricing beta feature, add the following environment variable:
```jsx
MEDUSA_FF_TAX_INCLUSIVE_PRICING=true
```
### Method Two: Using Backend Configurations
You can enable a feature by using the backend configurations in `medusa-config.js`. You can find [a feature flags key in the loader file](https://github.com/medusajs/medusa/tree/master/packages/medusa/src/loaders/feature-flags) its defined in. It is defined under the property `key` in the exported object.
For example, to enable the Tax-Inclusive Pricing beta feature, add the following to the exported object in `medusa-config.js`:
```jsx title=medusa-config.js
module.exports = {
featureFlags: {
tax_inclusive_pricing: true,
},
// ...
}
```
### Note About Precedence
The environment variables value has higher precedence over the backend configurations. So, if you use both these methods on your backend, the value of the environment variable will be used.
For example, if the value of the environment variable is set to `false`, but the value of the feature flag in the backend configurations is set to `true`, the feature flag will take the value of the environment variable and will be disabled.
### Running Migrations
As feature flags generally include adding new entities or making changes to entities in the database, you must run the migrations after enabling the feature flag:
```bash
medusa migrations run
```
:::info
You can learn more about migrations in this documentation.
:::
---
## Disable Feature Flags
Disabling feature flags follows the same process as enabling the feature flags. All you have to do is change the value in the environment variables or the backend configurations to `false`.
Once you disable a feature flag, all endpoints, entities, services, or other related classes and functionalities are disabled.
### Revert Migrations
If you had the feature flag previously enabled, and you want to disable this feature flag completely, you might need to revert the migrations you ran when you enabled it.
You can follow [this documentation to learn how to revert the last migration you ran](../../cli/reference.md#migrations).

View File

@@ -0,0 +1,35 @@
---
description: "Learn what a file service is in Medusa. A file service defines how files are stored in the Medusa Backend."
---
import DocCard from '@theme/DocCard';
import Icons from '@theme/Icon';
# File Service
In this document, youll learn what a file service is in Medusa.
## Introduction
A file service defines how files are stored in the Medusa Backend. Those files include products images and files used to import or export data.
Medusa Backend includes a default file service that acts as a placeholder, but does not actually perform any storage functionalities. So, you must either install one of the [existing file-service plugins](../../plugins/file-service/index.mdx), such as [MinIO](../../plugins/file-service/minio.md) or [S3](../../plugins/file-service/s3.md), or create your own file service if you want to utilize storage functionalities.
A file service is a TypeScript or JavaScript class that extends the `AbstractFileService` class from the core `@medusajs/medusa` package. By extending this class, the file service must implement the necessary methods that take care of general upload and download functionalities. The Medusa Backend then uses these methods when necessary, for example, when a product image is uploaded.
---
## Custom Development
Developers can create a custom file service with the desired functionality directly within the Medusa Core, in a plugin, or in a Commerce Module.
<DocCard item={{
type: 'link',
href: '#',
label: 'Create a File Service',
customProps: {
icon: Icons['academic-cap-solid'],
description: 'Learn how to create a file service.',
isSoon: true
}
}} />

View File

@@ -0,0 +1,27 @@
---
description: "Learn about Medusa's architecture and get a general overview of how all different tools work together."
---
# Medusa Architecture Overview
In this document, you'll get an overview of Medusa's architecture to better understand how all resources and tools work together.
## Architecture Overview
Medusa's core package `@medusajs/medusa` is a Node.js backend built on top of [Express](https://expressjs.com/). It combines all the [**Commerce Modules**](../../modules/overview.mdx) that Medusa provides. Commerce Modules are ecommerce features that can be used as building blocks in an ecommerce ecosystem. Product is an example of a Commerce Module.
![Medusa Core Architecture](https://res.cloudinary.com/dza7lstvk/image/upload/v1677607702/Medusa%20Docs/Diagrams/medusa-architecture-3_e385zk.jpg)
The backend connects to a **database**, such as [PostgreSQL](https://www.postgresql.org/), to store the ecommerce stores data. The tables in that database are represented by [**Entities**](../entities/overview.mdx), built on top of [Typeorm](https://typeorm.io/). Entities can also be reflected in the database using [**Migrations**](../entities/migrations/overview.mdx).
The retrieval, manipulation, and other utility methods related to that entity are created inside a [**Service**](../services/overview.mdx). Services are TypeScript or JavaScript classes that, similar along with other resources, can be accessed throughout the Medusa backend through [**dependency injection**](./dependency-injection.md).
The backend does not have any tightly-coupled frontend. Instead, it exposes [**Endpoints**](../endpoints/overview.mdx) which are REST APIs that frontends such as an admin or a storefront can use to communicate with the backend. Endpoints are [Express routes](https://expressjs.com/en/guide/routing.html).
Medusa also uses an [**Events Architecture**](../events/index.md) to trigger and handle events. Events are triggered when a specific action occurs, such as when an order is placed. To manage this events system, Medusa connects to a service that implements a pub/sub model, such as [Redis](https://redis.io/).
Events can be handled using [**Subscribers**](../events/subscribers.mdx). Subscribers are TypeScript or JavaScript classes that add their methods as handlers for specific events. These handler methods are only executed when an event is triggered.
You can create any of the resources in the backends architecture, such as entities, endpoints, services, and more, as part of your custom development without directly modifying the backend itself. The Medusa backend uses **loaders** to load the backends resources, as well as your custom resources and resources in [**Plugins**](../plugins/overview.mdx).
You can package your customizations into Plugins to reuse them in different Medusa backends or publish them for others to use. You can also install existing plugins into your Medusa backend.

View File

@@ -0,0 +1,600 @@
---
description: 'Learn what the dependency container is and how to use it in Medusa. Learn also what dependency injection is, and what the resources regsitered and their names are.'
---
# Dependency Injection
In this document, youll learn what the dependency injection is and how you can use it in Medusa.
## Introduction
### What is Dependency Injection
Dependency Injection is the act of delivering the required resources to a class. These resources are the classs dependencies. This is usually done by passing (or injecting) the dependencies in the constructor of the class.
Generally, all resources are registered in a container. Then, whenever a class depends on one of these resources, the system retrieves the resources from the container and injects them into the classs constructor.
### Medusas Dependency Container
Medusa uses a dependency container to register essential resources of the backend. You can then access these resources in classes and endpoints using the dependency container.
For example, if you create a custom service, you can access any other service registered in Medusa in your services constructor. That includes Medusas core services, services defined in plugins, or other services that you create on your backend.
You can load more than services in your Medusa backend. You can load the Entity Manager, logger instance, and much more.
### MedusaContainer
To manage dependency injections, Medusa uses [Awilix](https://github.com/jeffijoe/awilix). Awilix is an NPM package that implements dependency injection in Node.js projects.
When you run the Medusa backend, a container of the type `MedusaContainer` is created. This type extends the [AwilixContainer](https://github.com/jeffijoe/awilix#the-awilixcontainer-object) object.
The backend then registers all important resources in the container, which makes them accessible in classes and endpoints.
---
## Registered Resources
The Medusa backend scans the core Medusa package, plugins, and your files in the `dist` directory and registers the following resources:
:::note
Many resources are registered under their camel-case name. These resources are formatted by taking the name of the file, transforming it to camel case, then appending the folder name to the name. So, the `services/product.ts` service is registered as `productService`.
:::
<table class="reference-table">
<thead>
<tr>
<th>
Resource
</th>
<th>
Description
</th>
<th>
Registration Name
</th>
</tr>
</thead>
<tbody>
<tr>
<td>
Configurations
</td>
<td>
The configurations that are exported from `medusa-config.js`.
</td>
<td>
`configModule`
</td>
</tr>
<tr>
<td>
Services
</td>
<td>
Services that extend the `TransactionBaseService` class.
</td>
<td>
Each service is registered under its camel-case name. For example, the `ProductService` is registered as `productService`.
</td>
</tr>
<tr>
<td>
Entity Manager
</td>
<td>
An instance of Typeorms Entity Manager.
</td>
<td>
`manager`
</td>
</tr>
<tr>
<td>
Logger
</td>
<td>
An instance of Medusa CLIs logger. You can use it to log messages to the terminal.
</td>
<td>
`logger`
</td>
</tr>
<tr>
<td>
Single Payment Provider
</td>
<td>
An instance of every payment provider that extends the `AbstractPaymentService` class.
</td>
<td>
Every payment provider is registered under two names:
- Its camel-case name. For example, the `StripeProviderService` is registered as `stripeProviderService`.
- `pp_` followed by its identifier. For example, the `StripeProviderService` is registered as `pp_stripe`.
</td>
</tr>
<tr>
<td>
All Payment Providers
</td>
<td>
An array of all payment providers that extend the `AbstractPaymentService` class.
</td>
<td>
`paymentProviders`
</td>
</tr>
<tr>
<td>
Single Fulfillment Provider
</td>
<td>
An instance of every fulfillment provider that extends the `FulfillmentService` class.
</td>
<td>
Every fulfillment provider is registered under two names:
- Its camel-case name. For example, the `WebshipperFulfillmentService` is registered as `webshipperFulfillmentService`.
- `fp_` followed by its identifier. For example, the `WebshipperFulfillmentService` is registered as `fp_webshipper`.
</td>
</tr>
<tr>
<td>
All Fulfillment Providers
</td>
<td>
An array of all fulfillment providers that extend the `FulfillmentService` class.
</td>
<td>
`fulfillmentProviders`
</td>
</tr>
<tr>
<td>
Single Notification Provider
</td>
<td>
An instance of every notification provider that extends the `AbstractNotificationService` or the `BaseNotificationService` classes.
</td>
<td>
Every notification provider is registered under two names:
- Its camel-case name. For example, the `SendGridService` is registered as `sendGridService`.
- `noti_` followed by its identifier. For example, the `SendGridService` is registered as `noti_sendgrid`.
</td>
</tr>
<tr>
<td>
All Notification Providers
</td>
<td>
An array of all notification providers that extend the `AbstractNotificationService` or the `BaseNotificationService` classes.
</td>
<td>
`notificationProviders`
</td>
</tr>
<tr>
<td>
File Service
</td>
<td>
An instance of the class that extends the `FileService` class, if any.
</td>
<td>
The file service is registered under two names:
- Its camel-case name. For example, the `MinioService` is registered as `minioService`.
- `fileService`
</td>
</tr>
<tr>
<td>
Search Service
</td>
<td>
An instance of the class that extends the `AbstractSearchService` or the `SearchService` classes, if any.
</td>
<td>
The search service is registered under two names:
- Its camel-case name. For example, the `AlgoliaService` is registered as `algoliaService`.
- `searchService`
</td>
</tr>
<tr>
<td>
Single Tax Provider
</td>
<td>
An instance of every tax provider that extends the `AbstractTaxService` class.
</td>
<td>
The tax provider is registered under two names:
- Its camel-case name.
- `tp_` followed by its identifier.
</td>
</tr>
<tr>
<td>
All Tax Providers
</td>
<td>
An array of every tax provider that extends the `AbstractTaxService` class.
</td>
<td>
`taxProviders`
</td>
</tr>
<tr>
<td>
Oauth Services
</td>
<td>
An instance of every service that extends the `OauthService` class.
</td>
<td>
Each Oauth Service is registered under its camel-case name followed by `Oauth`.
</td>
</tr>
<tr>
<td>
Feature Flag Router
</td>
<td>
An instance of the `FlagRouter`. This can be used to list feature flags, set a feature flags value, or check if theyre enabled.
</td>
<td>
`featureFlagRouter`
</td>
</tr>
<tr>
<td>
Redis
</td>
<td>
An instance of the Redis client. If Redis is not configured, a fake Redis client is registered.
</td>
<td>
`redisClient`
</td>
</tr>
<tr>
<td>
Single Entity
</td>
<td>
An instance of every entity.
</td>
<td>
Each entity is registered under its camel-case name followed by Model. For example, the `CustomerGroup` entity is stored under `customerGroupModel`.
</td>
</tr>
<tr>
<td>
All Entities
</td>
<td>
An array of all database entities that is passed to Typeorm when connecting to the database.
</td>
<td>
`db_entities`
</td>
</tr>
<tr>
<td>
Repositories
</td>
<td>
An instance of each repository.
</td>
<td>
Each repository is registered under its camel-case name. For example, `CustomerGroupRepository` is stored under `customerGroupRepository`.
</td>
</tr>
<tr>
<td>
Single Batch Job Strategy
</td>
<td>
An instance of every class extending the `AbstractBatchJobStrategy` class.
</td>
<td>
Each batch job strategy is registered under three names:
- Its camel-case name. For example, `ProductImportStrategy` is registered as `productImportStrategy`.
- `batch_` followed by its identifier. For example, the `ProductImportStrategy` is registered under `batch_product-import-strategy`.
- `batchType_` followed by its batch job type. For example, the `ProductImportStrategy` is registered under `batchType_product-import`.
</td>
</tr>
<tr>
<td>
All Batch Job Strategies
</td>
<td>
An array of all classes extending the `AbstractBatchJobStrategy` abstract class.
</td>
<td>
`batchJobStrategies`
</td>
</tr>
<tr>
<td>
Tax Calculation Strategy
</td>
<td>
An instance of the class implementing the `ITaxCalculationStrategy` interface.
</td>
<td>
`taxCalculationStrategy`
</td>
</tr>
<tr>
<td>
Cart Completion Strategy
</td>
<td>
An instance of the class extending the `AbstractCartCompletionStrategy` class.
</td>
<td>
`cartCompletionStrategy`
</td>
</tr>
<tr>
<td>
Price Selection Strategy
</td>
<td>
An instance of the class implementing the `IPriceSelectionStrategy` interface.
</td>
<td>
`priceSelectionStrategy`
</td>
</tr>
<tr>
<td>
Strategies
</td>
<td>
An instance of strategies that arent of the specific types mentioned above and that are under the `strategies` directory.
</td>
<td>
Its camel-case name.
</td>
</tr>
</tbody>
</table>
---
## Loading Resources
This section covers how to load resources that the Medusa backend registers when it starts running.
### In Endpoints
To load resources, such as services, in endpoints, use the `req.scope.resolve` function. The function receives the registration name of the resource as a parameter.
For example:
```ts
const logger = req.scope.resolve("logger")
```
Please note that in endpoints some resources, such as repositories, are not available.
### In Classes
In classes such as services, strategies, or subscribers, you can load resources in the constructor function. The constructor receives an object of dependencies as a first parameter. Each dependency in the object should use the registration name of the resource that should be injected to the class.
For example:
```ts
import { OrderService } from "@medusajs/medusa"
class OrderSubscriber {
protected orderService: OrderService
constructor({ orderService }) {
this.orderService = orderService
}
}
```
---
## See Also
- [Create services](../services/create-service.md)
- [Create subscribers](../events/create-subscriber.md)

View File

@@ -0,0 +1,194 @@
---
description: 'Learn how to perform local development in the Medusa monorepo. This includes how to use the dev CLI tool and perform unit, integration, and plugin tests.'
---
# Local Development of Medusa Backend and Monorepo
In this document, youll learn how to customize Medusas core and run tests.
## Overview
As an open-source platform, Medusas core can be completely customized.
Whether you want to implement something differently, introduce a new future as part of Medusas core or any of the other packages, or contribute to Medusa, this guide helps you learn how to run Medusas integration tests, as well as test your own Medusa core in a local backend.
### Medusa Repository Overview
[Medusas repository on GitHub](https://github.com/medusajs/medusa) includes all packages related to Medusa under the [`packages` directory](https://github.com/medusajs/medusa/tree/master/packages). This includes the [core Medusa package](https://github.com/medusajs/medusa/tree/master/packages/medusa), the [JS Client](https://github.com/medusajs/medusa/tree/master/packages/medusa-js), the CLI tools, and much more.
All the packages are part of a [Yarn workspace](https://classic.yarnpkg.com/lang/en/docs/workspaces/). So, when you run a command in the root of the project, such as `yarn build`, it goes through all registered packages in the workspace under the `packages` directory and runs the `build` command in each of those packages.
---
## Prerequisites
### Yarn
When using and developing with the Medusa repository, its highly recommended that you use [Yarn](https://yarnpkg.com/getting-started/install) to avoid any errors or issues.
### Fork and Clone Medusas Repository
To customize Medusas core or contribute to it, you must first [fork](https://docs.github.com/en/get-started/quickstart/fork-a-repo) and then [clone](https://docs.github.com/en/get-started/quickstart/fork-a-repo#cloning-your-forked-repository) the [GitHub repository](https://github.com/medusajs/medusa).
### Install Dependencies and Build Packages
In the directory of the forked GitHub repository, run the following commands to install necessary dependencies then build all packages in the repository:
```bash
yarn install
yarn build
```
### Medusas Dev CLI tool
Medusa provides a CLI tool to be used for development. This tool facilitates testing your local installment and changes to Medusas core without having to publish the changes to NPM.
To install Medusas dev CLI tool:
```bash npm2yarn
npm install medusa-dev-cli -g
```
### Set the Location of the Medusa Repository
In the directory of your forked GitHub repository, run the following command to specify to the dev CLI tool the location of your Medusa repository:
```bash
medusa-dev --set-path-to-repo `pwd`
```
---
## Run Tests in the Repository
In this section, youll learn how to run tests in the Medusa repository. This is helpful after you customize any of Medusas packages and want to make sure everything is still working as expected.
### Set System Environment Variables
Before you can run the tests, make sure you set the following system environment variables:
```bash
DB_HOST=<YOUR_DB_HOST>
DB_USERNAME=<YOUR_DB_USERNAME>
DB_PASSWORD=<YOUR_PASSWORD>
```
### Run Unit Tests
To run unit tests in all packages in the Medusa repository, run the following command in the root directory of the repository:
```bash
yarn test
```
This runs the `test` script defined in the `package.json` file of each package under the `packages` directory.
Alternatively, if you want to run the unit tests in a specific package, you can run the `test` command in the directory of that package.
For example, to run the unit tests of the Medusa core:
```bash
cd packages/medusa
yarn test
```
### Run API Integration Tests
API integration tests are used to test out Medusas core endpoints.
To run the API integration tests, run the following command in the root directory of the repository:
```bash
yarn test:integration:api
```
### Run Plugin Integration Tests
Plugin integration tests are used to test out Medusas official plugins, which are also stored in the `packages` directory in the repository.
To run the plugin integration tests, run the following command in the root directory of the repository:
```bash
yarn test:integration:plugins
```
---
## Test in a Local Backend
Using Medusas dev CLI tool, you can test any changes you make to Medusas packages in a local backend installation. This eliminates the need to publish these packages on NPM publicly to be able to use them.
Medusas dev CLI tool scans and finds the Medusa packages used in your Medusa backend. Then, it copies the files of these packages from the `packages` directory in the Medusa repository into the `node_modules` directory of your Medusa backend.
:::info
Medusas Dev CLI tool uses the [path you specified earlier](#set-the-location-of-the-medusa-repository) to copy the files of the packages.
:::
### Copy Files to Local Backend
To test in a local backend:
1. Change to the directory of the backend you want to test your changes in:
```bash
cd medusa-backend
```
2\. Run the following command to copy the files from the `packages` directory of your Medusa repository into `node_modules`:
```bash
medusa-dev
```
By default, Medusas dev CLI runs in watch mode. So, it copies the files when you first run it. Then, whenever you make changes in the `dist` directory of the packages in the Medusa repository, it copies the changed files again.
### Watch and Compile Changes
While the above command is running, it's recommended to run the `watch` command inside the directory of every package you're making changes to.
The combination of these two commands running at the same time will compile the package into the `dist` directory of the package, then copy the compiled changes into your local backend.
For example, if you're making changes in the `medusa` package, run the following command inside the directory of the `medusa` package:
```bash title=packages/medusa
yarn watch
```
Make sure the `medusa-dev` command is also running to copy the changes automatically.
Alternatively, you can manually run the `build` command every time you want to compile the changes:
```bash title=packages/medusa
yarn build
```
### CLI Options
Here are some options you can use to customize how Medusas dev CLI tool works:
- `--scan-once` or `-s`: Copies files only one time then stops processing. If you make any changes after running the command with this option, you have to run the command again.
```bash
medusa-dev -s
```
- `--quiet` or `-q`: Disables showing any output.
```bash
medusa-dev -q
```
- `--packages`: Only copies specified packages. It accepts at least one package name. Package names are separated by a space.
```bash
medusa-dev --packages @medusajs/medusa-cli medusa-file-minio
```
---
## See Also
- [Create a Plugin](../plugins/create.md)
- [Contribution Guidelines](https://github.com/medusajs/medusa/blob/master/CONTRIBUTING.md)

View File

@@ -0,0 +1,342 @@
---
description: 'Learn how to create a notification provider in Medusa. This guide explains the different methods available in a Notification provider.'
addHowToData: true
---
# How to Create a Notification Provider
In this document, youll learn how to create a Notification Provider in Medusa.
:::note
If youre unfamiliar with the Notification architecture in Medusa, it is recommended to check out the [architecture overview](./overview.mdx) first.
:::
## Prerequisites
Before you start creating a Notification Provider, you need to either install a [Medusa backend](../backend/install.mdx), or create it in a [plugin](../plugins/overview.mdx).
You also need to [setup Redis](../backend/prepare-environment.mdx#redis) and [configure it with the Medusa backend](../backend/configurations.md#redis) to test out the Notification provider.
---
## Create a Notification Provider
Creating a Notification Provider is as simple as creating a TypeScript or JavaScript file in `src/services`. The name of the file is the name of the provider (for example, `sendgrid.ts`). A Notification Provider is essentially a Service that extends the `AbstractNotificationService` from `@medusajs/medusa`.
For example, create the file `src/services/email-sender.ts` with the following content:
```ts title=src/services/email-sender.ts
import { AbstractNotificationService } from "@medusajs/medusa"
import { EntityManager } from "typeorm"
class EmailSenderService extends AbstractNotificationService {
protected manager_: EntityManager
protected transactionManager_: EntityManager
sendNotification(
event: string,
data: unknown,
attachmentGenerator: unknown
): Promise<{
to: string;
status: string;
data: Record<string, unknown>;
}> {
throw new Error("Method not implemented.")
}
resendNotification(
notification: unknown,
config: unknown,
attachmentGenerator: unknown
): Promise<{
to: string;
status: string;
data: Record<string, unknown>;
}> {
throw new Error("Method not implemented.")
}
}
export default EmailSenderService
```
Where `EmailSenderService` is the name of your Notification Provider Service.
Notification Providers must extend `NotificationService` from `medusa-interfaces`.
:::info
Following the naming convention of Services, the name of the file should be the slug name of the Notification Provider, and the name of the class should be the camel case name of the Notification Provider suffixed with “Service”. In the example above, the name of the file should be `email-sender.js`. You can learn more in the [service documentation](../services/create-service.md).
:::
### identifier
Notification Provider Services must have a static property `identifier`.
The `NotificationProvider` entity has 2 properties: `identifier` and `is_installed`. The value of the `identifier` property in the Service class is used when the Notification Provider is created in the database.
The value of this property is also used later when you want to subscribe the Notification Provider to events in a Subscriber.
For example, in the class you created in the previous code snippet you can add the following property:
```ts
class EmailSenderService extends AbstractNotificationService {
static identifier = "email-sender"
// ...
}
```
### constructor
You can use the `constructor` of your Notification Provider to have access to different Services in Medusa through dependency injection.
You can also use the constructor to initialize your integration with the third-party provider. For example, if you use a client to connect to the third-party providers APIs, you can initialize it in the constructor and use it in other methods in the Service.
Additionally, if youre creating your Notification Provider as an external plugin to be installed on any Medusa backend and you want to access the options added for the plugin, you can access it in the constructor. The options are passed as a second parameter.
:::info
You can learn more about plugins and how to create them in the [Plugins](../plugins/overview.mdx) documentation.
:::
Continuing on with the previous example, if you want to use the [`OrderService`](../../references/services/classes/OrderService.md) later when sending notifications, you can inject it into the constructor:
```ts
import {
AbstractNotificationService,
OrderService,
} from "@medusajs/medusa"
class EmailSenderService extends AbstractNotificationService {
protected manager_: EntityManager
protected transactionManager_: EntityManager
static identifier = "email-sender"
protected orderService: OrderService
constructor(container, options) {
super(container)
// you can access options here in case you're
// using a plugin
this.orderService = container.orderService
}
// ...
}
```
### sendNotification
When an event is triggered that your Notification Provider is registered as a handler for, the [`NotificationService`](../../references/services/classes/NotificationService.md) in Medusas core will execute the `sendNotification` method of your Notification Provider.
In this method, you can perform the necessary operation to send the Notification. Following the example above, you can send an email to the customer when they place an order.
This method receives three parameters:
1. `eventName`: This is the name of the event that was triggered. For example, `order.placed`.
2. `eventData`: This is the data payload of the event that was triggered. For example, if the `order.placed` event is triggered, the `eventData` object contains the property `id` which is the ID of the order that was placed.
3. `attachmentGenerator`: If youve previously attached a generator to the `NotificationService` using the [`registerAttachmentGenerator`](../../references/services/classes/NotificationService.md#registerattachmentgenerator) method, you have access to it here. You can use the `attachmentGenerator` to generate on-demand invoices or other documents. The default value of this parameter is null.
:::info
You can learn more about what events are triggered in Medusa and their data payload in the [Events List](../events/events-list.md) documentation.
:::
This method must return an object containing two properties:
1. `to`: a string that represents the receiver of the Notification. For example, if you sent an email to the customer then `to` is the email address of the customer. In other cases, it might be a phone number or a username.
2. `data`: an object that contains the data used to send the Notification. For example, if you sent an order confirmation email to the customer, then the `data` object might include the order items or the subject of the email. This `data` is necessary if the notification is resent later as you can use the same data.
Continuing with the previous example you can have the following implementation of the `sendNotification` method:
```ts
class EmailSenderService extends AbstractNotificationService {
// ...
async sendNotification(
event: string,
data: any,
attachmentGenerator: unknown
): Promise<{
to: string;
status: string;
data: Record<string, unknown>;
}> {
if (event === "order.placed") {
// retrieve order
const order = await this.orderService.retrieve(data.id)
// TODO send email
console.log("Notification sent")
return {
to: order.email,
status: "done",
data: {
// any data necessary to send the email
// for example:
subject: "You placed a new order!",
items: order.items,
},
}
}
}
}
```
In this code snippet, you check first if the event is `order.placed`. This can be helpful if youre handling multiple events using the same Notification Provider.
You then retrieve the order using the ID and send the email. Here, the logic related to sending the email is not implemented as it is generally specific to your Notification Provider.
Finally, you return an object with the `to` property set to the customer email and the `data` property is an object that contains data necessary to send the email such as a `subject` or `items`.
:::note
The `to` and `data` properties are used in the `NotificationService` in Medusas core to create a new `Notification` record in the database. You can learn more about the `Notification` entity in the [Architecture Overview](./overview.mdx#notification-entity-overview) documentation.
:::
### resendNotification
Using the [Resend Notification endpoint](/api/admin/#tag/Notification/operation/PostNotificationsNotificationResend), an admin user can resend a Notification to the customer. The [`NotificationService`](../../references/services/classes/NotificationService.md) in Medusas core then executes the `resendNotification` method in your Notification Provider.
This method receives three parameters:
1. `notification`: This is the original Notification record that was created after you sent the notification with `sendNotification`. You can get an overview of the entity and its attributes in the [architecture overview](./overview.mdx#notification-entity-overview), but most notably it includes the `to` and `data` attributes which are populated originally using the `to` and `data` properties of the object you return in `sendNotification`.
2. `config`: In the Resend Notification endpoint you may specify an alternative receiver of the notification using the `to` request body parameter. For example, you may want to resend the order confirmation email to a different email. If thats the case, you have access to it in the `config` parameter object. Otherwise, `config` will be an empty object.
3. `attachmentGenerator`: If youve previously attached a generator to the Notification Service using the [`registerAttachmentGenerator`](../../references/services/classes/NotificationService.md#registerattachmentgenerator) method, you have access to it here. You can use the `attachmentGenerator` to generate on-demand invoices or other documents. The default value of this parameter is null.
Similarly to the `sendNotification` method, this method must return an object containing two properties:
1. `to`: a string that represents the receiver of the Notification. You can either return the same `to` available in the `notification` parameter or the updated one in the `config` parameter.
2. `data`: an object that contains the data used to send the Notification. You can either return the same `data` in the `notification` parameter or make any necessary updates to it.
Continuing with the previous example you can have the following implementation of the `resendNotification` method:
```ts
class EmailSenderService extends AbstractNotificationService {
// ...
async resendNotification(
notification: any,
config: any,
attachmentGenerator: unknown
): Promise<{
to: string;
status: string;
data: Record<string, unknown>;
}> {
// check if the receiver should be changed
const to: string = config.to ? config.to : notification.to
// TODO resend the notification using the same data
// that is saved under notification.data
console.log("Notification resent")
return {
to,
status: "done",
data: notification.data, // make changes to the data
}
}
}
```
In the above snippet, you check if the `to` should be changed by checking if the `config` parameter has a `to` property. Otherwise, you keep the same `to` address stored in the `notification` parameter.
You then resend the email. Here, the logic related to sending the email is not implemented as it is generally specific to your Notification Provider.
Finally, you return an object with the `to` property set to the email (either new or old) and the `data` property is the same data used before to send the original notification. you can alternatively make any changes to the data.
:::note
The `to` and `data` properties are used in the `NotificationService` in Medusas core to create a new `Notification` record in the database. No changes are made to the original `Notification` record created after the `sendNotification` method. This new record is associated with the original `Notification` record using the `parent_id` attribute in the entity. You can learn more about the `Notification` entity in the [Architecture Overview](./overview.mdx#notification-entity-overview) documentation.
:::
---
## Create a Subscriber
After creating your Notification Provider Service, you must create a Subscriber that registers this Service as a notification handler of events.
:::note
This section will not cover the basics of Subscribers. You can read the [Subscribers](../events/create-subscriber.md) documentation to learn more about them and how to create them.
:::
Following the previous example, to make sure the `email-sender` Notification Provider handles the `order.placed` event, create the file `src/subscribers/notification.js` with the following content:
```ts title=src/subscribers/notification.js
class NotificationSubscriber {
constructor({ notificationService }) {
notificationService.subscribe(
"order.placed",
"email-sender"
)
}
// ...
}
export default NotificationSubscriber
```
This subscriber accesses the `notificationService` using dependency injection. The `notificationService` contains a `subscribe` method that accepts 2 parameters. The first one is the name of the event to subscribe to, and the second is the identifier of the Notification Provider that is subscribing to that event.
:::tip
Notice that the value of the `identifier` static property defined in the `EmailSenderService` is used to register the Notification Provider to handle the `order.placed` event.
:::
---
## Test Sending Notifications with your Notification Provider
Make sure you've configured Redis with your Medusa backend as explained in the [Prerequisites](#prerequisites) section and that the Redis service is running.
Then, start by running your Medusa backend:
```bash npm2yarn
npm run start
```
Then, place an order either using the [REST APIs](/api/store) or using the storefront.
:::tip
If you dont have a storefront installed you can get started with either the [Next.js](../../starters/nextjs-medusa-starter.mdx) or [Gatsby](../../starters/gatsby-medusa-starter.mdx) starter storefronts in minutes.
:::
After placing an order, you can see in your console the message “Notification Sent”. If you added your own notification sending logic, you should receive an email or alternatively the type of notification youve set up.
---
## Test Resending Notifications with your Notification Provider
To test resending a notification, first, retrieve the ID of the notification you just sent using the [List Notifications admin endpoint](/api/admin/#tag/Notification/operation/GetNotifications). You can pass as a body parameter the `to` or `event_name` parameters to filter out the notification you just sent.
:::tip
You must be authenticated as an admin user before sending this request. You can use the [Authenticate a User](/api/admin) endpoint to get authenticated.
:::
![List Notifications Request](https://res.cloudinary.com/dza7lstvk/image/upload/v1668001650/Medusa%20Docs/Screenshots/iF1rZX1_msps2t.png)
Then, send a request to the [Resend Notification](/api/admin/#tag/Notification/operation/PostNotificationsNotificationResend) endpoint using the ID retrieved from the previous request. You can pass the `to` parameter in the body to change the receiver of the notification. You should see the message “Notification Resent” in your console and if you implemented your own logic for resending the notification it will be resent.
![Resend Notifications Request](https://res.cloudinary.com/dza7lstvk/image/upload/v1668001659/Medusa%20Docs/Screenshots/0zFfPed_og7one.png)
This request returns the same notification object as the List Notifications endpoint, but it now has a new object in the `resends` array. This is the resent notification. If you supplied a `to` parameter in the request body, you should see its value in the `to` property of the resent notification object.
---
## See Also
- [Create a Plugin](../plugins/create.md)

View File

@@ -0,0 +1,109 @@
---
description: 'Learn about the Notificaiton architecture in Medusa and the automation flow. The Notification Architecture is made up of the Notification Provider and Notification.'
---
import DocCard from '@theme/DocCard';
import Icons from '@theme/Icon';
# Notification Architecture Overview
This document gives an overview of the notification architecture and how it works.
## Introduction
Medusa provides a Notification API to mainly handle sending and resending notifications when an event occurs. For example, sending an email to the customer when they place an order.
The Notification architecture is made up of two main components: the Notification Provider and the Notification. Simply put, the Notification Provider handles the sending and resending of a Notification.
---
## Notification Provider
A Notification Provider is a provider that handles sending and resending of notifications. You can either create and integrate your own provider or install a Notification Provider through a third-party plugin.
An example of a notification provider is SendGrid. When an order is placed, the SendGrid plugin sends an email to the customer.
### How Notification Provider is Created
A Notification Provider is essentially a Medusa [Service](../services/create-service.md) with a unique identifier, and it extends the [`NotificationService`](../../references/services/classes/NotificationService.md) provided by the `medusa-interfaces` package. It can be created as part of a [Plugin](../plugins/overview.mdx), or it can be created just as a Service file in your Medusa backend.
As a developer, you mainly work with the Notification Provider when integrating a third-party service that handles notifications in Medusa.
When you run your Medusa backend, the Notification Provider is registered in your backend. If it's a new Notification Provider, it will be inserted into the `notification_provider` table in your database.
### NotificationProvider Entity Overview
The `NotificationProvider` entity only has 2 attributes: `id` and `is_installed`.
`id` is the value of the static property `identifier` defined inside the notification Service class.
`is_installed` indicates whether the Notification Provider is installed or not. When you install a Notification Provider, the value of this attribute is `true`.
If you installed a Notification provider and then removed the Service files or plugin that registered the Notification Provider, the Notification Provider remains in your database, but the value of the `is_installed` field changes to `false`.
---
## Notification
A notification is a form of an alert sent to the customers or users to inform them of an action that has occurred. For example, if an order is placed, the notification, in this case, can be an email that confirms their order and lists the order details.
Notifications can take on other forms such as an SMS or a Slack message.
### How Notification is Created
Notifications are created in the `NotificationService` class in Medusas core after the Notification has been handled by the Notification Provider.
The data and additional details that the Notification Provider returns to the `NotificationService` is used to fill some of the attributes of the Notification in the database.
A Notification also represents a resent notification. So, when a notification is resent, a new one is created that references the original Notification as a parent. This Notification is also created by the `NotificationService` class.
### Notification Entity Overview
The two most important properties in the [`Notification`](../../references/entities/classes/Notification.md) entity are the `to` and `data` properties.
The `to` property is a string that represents the receiver of the Notification. For example, if the Notification was sent to an email address, the `to` property holds the email address the Notification was sent to.
The `to` property can alternatively be a phone number or a chat username. It depends on the Notification Provider and how it sends the Notification.
The `data` property is an object that holds all the data necessary to send the Notification. For example, in the case of an order confirmation Notification, it can hold data related to the order.
The `data` property is useful when a notification is resent later. The same `data` can be used to resend the notification.
In the case of resent notifications, the resent notification has a `parent_id` set to the ID of the original Notification. The value of the `parent_id` property in the original Notification is `null`.
The `Notification` entity has some properties that determine the context of this Notification. This includes the `event_name` property which is the event that triggered the sending of this notification.
Additionally, the `resource_type` property is used to determine what resource this event is associated with. For example, if the `event_name` is `order.placed`, the `resource_type` is `order`.
You can also access the specific resource using the `resource_id` property, which is the ID of the resource. So, in case of the `order.placed` event, the `resource_id` is the ID of the order that was created.
The `Notification` entity also includes properties related to the receiver of the Notification. In case the receiver is a customer, the `customer_id` property is used to identify which customer.
---
## Automating Flows with Notifications
With Medusa you can create notifications as a reaction to a wide spectrum of events, allowing you to automate communication and processes.
An example of a flow that can be implemented using Medusa's Notification API is automated return flows:
- A customer requests a return by sending a `POST` request to the `/store/returns` endpoint.
- The Notification Provider listens to the `order.return_requested` event and sends an email to the customer with a return invoice and return label generated by the Fulfillment Provider.
- The customer returns the items triggering the `return.recieved` event.
- The Notification Provider listens to the `return.received` event and sends an email to the customer with confirmation that their items have been received and that a refund has been issued.
---
## Custom Development
Developers can create custom notification providers in the Medusa backend, a plugin, or in a Commerce Module.
<DocCard item={{
type: 'link',
href: '/development/notification/create-notification-provider',
label: 'Create a Notification Provider',
customProps: {
icon: Icons['academic-cap-solid'],
description: 'Learn how to create a notification provider in Medusa.'
}
}}
/>

View File

@@ -0,0 +1,203 @@
---
description: "Learn about development with Medusa, fundamental concepts, and more."
hide_table_of_contents: true
---
import DocCardList from '@theme/DocCardList';
import Icons from '@theme/Icon';
# Medusa Development
This part of the documentation provides you with the fundamental concepts and guides that can help you build and customize commerce applications with Medusa.
## Introduction
Medusa is a set of tools that developers can use to build digital commerce applications. Whether you want to offer unique customer experiences, create powerful automations, or build rich commerce applications like marketplaces, Medusa provides all the necessary tools.
Other ecommerce platforms offer a finite set of features accessible through an API. Medusa is different because it provides building blocks for businesses and developers to build commerce features. This means that you can extend your commerce API for your exact needs.
Medusa's building blocks ship as NPM packages of the following types:
- [Commerce Modules](../modules/overview.mdx), which are isolated commerce logic for different domains. For example, an Inventory Module.
- A core package responsible for orchestrating the different commerce modules and exposing REST APIs.
---
## How Does Medusa Work
The core package is the NPM package `@medusajs/medusa`. It's a Node.js server built with Express and other tools that offer functionalities for managing events, caching, job queues, and more.
The core package has two main objectives.
### Orchestrate Commerce Modules
When you build a commerce application with Medusa, youll typically interact with more than one commerce module. The core package manages relationships between modules, and forwarding calls to modules at the right time during business logic execution.
For example, imagine an Inventory module that contains lightweight logic to increment and decrement stock levels for a Stock-Keeping Unit (SKU). In a commerce application, you typically want to associate the stock levels with a specific product. Medusa offers both an Inventory module and a Product module, and the core package creates associations between these modules and executing the related business logic. So, the core package contains code similar to this:
```ts
function handler(req, res) {
// ...
// associate a product with an inventory item
const product = await productService.create(data)
const inventoryItem = await inventoryService.create(
inventoryData
)
await productVariantInventoryService.associate(
product.id,
inventoryItem.id
)
// ...
}
```
### Expose REST APIs
The goal of orchestrating the modules is to expose an API that client applications, like websites or apps, can consume. By default, Medusas core package exposes a REST API that offers commerce functionalities similar to what other platforms give you.
The core package also holds the logic that allows developers to extend and add custom endpoints, among other available customizations.
---
## Backend First Steps
<DocCardList colSize={4} items={[
{
type: 'link',
href: '/development/backend/install',
label: 'Backend Quickstart',
customProps: {
icon: Icons['server-stack-solid'],
description: 'Learn how to install a Medusa backend and the available next steps.'
}
},
{
type: 'link',
href: '/development/backend/prepare-environment',
label: 'Prepare Environment',
customProps: {
icon: Icons['tools-solid'],
description: 'Install tools that supercharge development with Medusa.'
}
},
{
type: 'link',
href: '/development/backend/configurations',
label: 'Configure Backend',
customProps: {
icon: Icons['cog-six-tooth-solid'],
description: 'Learn how to configure the Medusa backend.'
}
},
]} />
---
## Understanding Fundamental Concepts
These concepts will guide you through your development and building customization with Medusa.
<DocCardList colSize={4} items={[
{
type: 'link',
href: '/development/entities/overview',
label: 'Entities',
customProps: {
icon: Icons['academic-cap-solid'],
description: 'A class representation of database tables to handle commerce data.'
}
},
{
type: 'link',
href: '/development/endpoints/overview',
label: 'Endpoints',
customProps: {
icon: Icons['academic-cap-solid'],
description: 'REST APIs that frontends consume to communicate with the backend.'
}
},
{
type: 'link',
href: '/development/services/overview',
label: 'Services',
customProps: {
icon: Icons['academic-cap-solid'],
description: 'Utility classes associated with Entities or a specific functionality.'
}
},
{
type: 'link',
href: '/development/events/index',
label: 'Events and Subscribers',
customProps: {
icon: Icons['academic-cap-solid'],
description: 'Perform an asynchronous task when an action occurs.'
}
},
{
type: 'link',
href: '/development/scheduled-jobs/overview',
label: 'Scheduled Jobs',
customProps: {
icon: Icons['academic-cap-solid'],
description: 'Automate tasks to be executed at specified times.'
}
},
{
type: 'link',
href: '/development/plugins/overview',
label: 'Plugins',
customProps: {
icon: Icons['academic-cap-solid'],
description: 'Publish customizations as NPM packages to be reused.'
}
},
]} />
---
## Medusa Use Cases
To better understand how you can use Medusa, here are some common use cases that Medusa is the ideal solution for.
### Ecommerce Building Blocks
Developers can set up the core package and handpick the Commerce Modules they want to use. This gives them great flexibility in choosing the features they want to provide in their ecommerce store, while utilizing the powerful architecture in the core package.
![Ecommerce Building Blocks](https://res.cloudinary.com/dza7lstvk/image/upload/v1678954316/Medusa%20Docs/Diagrams/ecommerce-building-blocks_llgnn2.jpg)
Developers can modify and tailor the modules that Medusa offers to their use case. They can also create custom Modules to implement new features. All these modules become building blocks that shape their ecommerce system.
### Medusa in Microservices Architectures
Medusas Commerce Modules can be used in isolation from the core package and within a larger ecosystem. For example, you can use Medusas Cart module within a blog to allow readers to buy merch.
![Medusa in Microservices Architecture](https://res.cloudinary.com/dza7lstvk/image/upload/v1678954316/Medusa%20Docs/Diagrams/microservices-architecture-use-case_vubgno.jpg)
Developers can benefit from Medusas Modules that provide essential ecommerce features while maintaining the ecommerce ecosystem of their choice. Commerce Modules can be installed in your setup as NPM packages.
### Vertical Ecommerce Platforms
A Vertical Ecommerce Platform is a platform that provides features and functionalities specialized for a type of business sector. For example, a platform for pharmaceuticals.
Developers can use Medusa to build a vertical ecommerce platform as it provides the stepping stones that eliminate the need to reinvent the wheel for basic ecommerce features, but are customizable enough to be changed for their use case.
### Out-of-Box APIs
Since Medusas Commerce Modules are NPM packages, they can be installed and used in any JavaScript project.
By installing a Module in your project and expose its APIs based on the framework youre using, you can get ecommerce REST APIs right from your frontend framework without having to create a separate project.
### Full-Fledged Ecommerce System
Developers can use Medusas toolkit to create their ecommerce system. With the use of the [create-medusa-app](../create-medusa-app.mdx) command, developers can set up a Medusa Backend, Medusa admin, and a storefront.
![Full-Fledged Ecommerce System](https://res.cloudinary.com/dza7lstvk/image/upload/v1678954316/Medusa%20Docs/Diagrams/fully-fledged-ecom_qqwraq.jpg)
Developers can still benefit from customization opportunities here that Medusa provides. This includes creating resources such as endpoints and services, creating plugins, integrating third-party services, create a custom storefront, and more.
### Your Own Use Case
Medusas vision is to allow developers to build anything they want using it. There are no limitations to what you can build and the ideas you can come up with. If you have an idea, you can use Medusas tools to start building it.

View File

@@ -0,0 +1,321 @@
---
description: 'Learn how to create a plugin in Medusa. This guide explains how to develop, configure, and test a plugin.'
addHowToData: true
---
# How to Create a Plugin
In this document, youll learn how to create a plugin and some tips for develoment. If youre 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 dont 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
medusa new medusa-plugin-custom
```
Where `medusa-plugin-custom` is the name of the plugin youre 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": {
"@medusajs/medusa": "^1.3.1",
"@medusajs/medusa-cli": "^1.3.0",
"medusa-fulfillment-manual": "^1.1.31",
"medusa-interfaces": "^1.3.0",
"medusa-payment-manual": "^1.0.16",
"medusa-payment-stripe": "^1.1.38",
"typeorm": "^0.2.36"
},
"devDependencies": {
"@babel/cli": "^7.14.3",
"@babel/core": "^7.14.3",
"@babel/preset-typescript": "^7.14.5",
"babel-preset-medusa-package": "^1.1.19"
}
```
For a plugin, some dependencies are not necessary. You 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.
Additionally, you can remove `@medusajs/medusa-cli` as you dont need to use the Medusa CLI while developing a plugin.
Once youre done making these changes, re-run the install command to update your `node_modules` directory:
```bash npm2yarn
npm install
```
### 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, it's recommended to change the `build` command and add a new `watch` command:
```json title=package.json
"scripts": {
"build": "babel src --out-dir . --ignore **/__tests__ --extensions \".ts,.js\"",
"watch": "babel -w src --out-dir . --ignore **/__tests__ --extensions \".ts,.js\""
}
```
The change to the `build` command ensures that the built files are placed as explained in the [plugin structure section](#plugin-structure). The `watch` command makes the [testing of the plugin](#test-your-plugin) easier.
:::caution
If you don't make changes to the `build` and `watch` commands, please be aware of the [expected plugin structure](#plugin-structure).
:::
---
## Develop your Plugin
Now, You can start developing your plugin. This can include adding services, endpoints, entities, 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, etc...
However, before you test the changes on a Medusa backend or publish your plugin, you must transpile your files, which moves them into the root of your plugin directory.
For example, if you have an endpoint in `src/api/index.js`, after running the `build` or `watch` commands [as defined earlier](#change-scripts), the file should be transpiled into `api/index.js` in your plugin's root.
If files and directories aren't placed in the root of your plugin, the Medusa backend won't detect or load them.
An example of a plugin's directory before testing or publishing:
```bash noReport
medusa-plugin-custom
|
|_ _ _ api
| |
| |_ _ _ index.js
|
|_ _ _ migrations
| |
| |_ _ _ <TIMESTAMP>_UserChanged.js
|
|_ _ _ src
| |
| |_ _ _ api
| | |
| | |_ _ _ index.ts
| |
| |_ _ _ migrations
| |
| |_ _ _ <TIMESTAMP>_UserChanged.ts
|
|_ _ _ package.json
//... other files
```
### Development Resources
This guide doesn't cover how to create different files and components. If youre interested in learning how to do that, you can check out these guides:
- How to [create endpoints](../endpoints/create.md)
- How to [create a service](../services/create-service.md)
- How to [create a subscriber](../events/create-subscriber.md)
- How to [create an entity](../entities/create.md)
- How to [create a migration](../entities/migrations/create.md)
---
## 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 youre integrating.
To pass a plugin its configurations on a Medusa backend, you have to add it to the `plugins` array in `medusa-config.js`:
```jsx 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:
```jsx 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:
```jsx 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.
:::
---
## Test Your Plugin
While you develop your plugin, youll 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 youre running the `watch` command, you dont 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`:
```jsx 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 plugins 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.md).

View File

@@ -0,0 +1,85 @@
---
description: 'Learn what Plugins are and how they are used in Medusa. Plugins are re-usable customizations that can be added to a Medusa backend.'
---
import DocCardList from '@theme/DocCardList';
import Icons from '@theme/Icon';
# Plugins
In this document, youll get an overview of plugins in Medusa, where to find them, and how to install them. If you want to learn how to create a plugin, check out [this guide](create.md) instead.
## Overview
Medusa was built with flexibility and extendibility in mind. All different components and functionalities in Medusa are built with an abstraction layer that gives developers the freedom to choose what services they want to use or how to implement a certain component in their ecommerce store.
Developers can use plugins to take advantage of this abstraction, flexibility, and extendibility. Plugins allow developers to implement custom features or integrate third-party services into Medusa.
For example, if you want to use Stripe as a payment provider in your store, then you can install the Stripe plugin on your backend and use it.
An alternative approach is developing a custom way of handling payment on your ecommerce store. Both approaches are achievable by either creating a plugin or using an existing plugin.
Plugins run within the same process as the core Medusa backend eliminating the need for extra backend capacity, infrastructure, and maintenance. As a result, plugins can use all other services as dependencies and access the database.
---
## Using Existing Plugins
### Official Plugins
Medusa has official plugins that cover different aspects and functionalities such as payment, Content Management System (CMS), fulfillment, and notifications. You can check out the available plugins under the [Plugins section of this documentation](../../plugins/overview.mdx).
:::tip
To feature your plugin in our repository, you can send a pull request that adds your plugin into the `packages` directory. Our team will review your plugin and, if approved, will merge the pull request and add your plugin in the repository.
:::
### Community Plugins
You can find community plugins by [searching NPM for the `medusa` or `medusa-plugin` keywords](https://www.npmjs.com/search?q=keywords%3Amedusa%2Cmedusa-plugin).
You can also check the [Awesome Medusa repository](https://github.com/adrien2p/awesome-medusajs#plugins) for a list of community plugins among other resources.
## How to Install a Plugin
To install an existing plugin, in your Medusa backend run the following command:
```bash npm2yarn
npm install <plugin_name>
```
Where `<plugin_name>` is the package name of the plugin. For example, if youre installing the Stripe plugin `<plugin_name>` is `medusa-payment-stripe`.
### Plugin Configuration
If youre installing an official plugin from the Medusa repository, you can find in its `README.md` file a list of configurations that are either required or optional. You can also refer to the documentation related to that plugin for more details on how to install, configure, and use it.
For community plugins, please refer to the installation instructions of that plugin to learn about any required configurations.
---
## Custom Development
Developers can create plugins and reuse them across different Medusa backends. They can also share them with the community to help out other developers.
<DocCardList colSize={6} items={[
{
type: 'link',
href: '/development/plugins/create',
label: 'Create a Plugin',
customProps: {
icon: Icons['academic-cap-solid'],
description: 'Learn how to create plugins in Medusa.'
}
},
{
type: 'link',
href: '/development/plugins/publish',
label: 'Publish a Plugin',
customProps: {
icon: Icons['academic-cap-solid'],
description: 'Learn how to publish a plugin to NPM.'
}
},
]} />

View File

@@ -0,0 +1,154 @@
---
description: 'Learn how to publish a Medusa plugin to NPM. This guide lists some check lists to ensure you have implemented before publishing, as well as required steps.'
addHowToData: true
---
# How to Publish a Plugin
In this document, you'll learn how to publish a Medusa plugin to NPM and what are some requirements to keep in mind before publishing.
## Prerequisites
If you haven't created a plugin yet, please check [this guide to learn how to create a plugin](./create.md).
---
## Prepare the Plugin
### package.json Checklist
Before publishing your plugin, make sure you've set the following fields in your plugin's package.json:
- `name`: The name of your plugin. By convention, all plugin names start with `medusa` followed by a descriptive name of what the plugin does. For example, `medusa-payment-stripe`.
- `description`: A short description of what the plugin does.
- `author`: Your name or your company's name.
- `repository`: This includes details about the repository that holds the source code of the plugin. It's an object that holds the following properties:
- `type`: Should be `git`.
- `url`: The URL to the repository (for example, the GitHub repository holding the code of your plugin).
- `keywords`: An array of keywords that are related to the plugin. It's required for all Medusa plugins to use the keywords `medusa-plugin`. Other recommended keywords are:
- `medusa-plugin-analytics`: For plugins that add analytics functionalities or integrations.
- `medusa-plugin-cms`: For plugins that add CMS functionalities or integrations.
- `medusa-plugin-notification`: For plugins that add notification functionalities or integrations.
- `medusa-plugin-payment`: For plugins that add payment functionalities or integrations.
- `medusa-plugin-search`: For plugins that add search functionalities or integrations.
- `medusa-plugin-shipping`: For plugins that add shipping functionalities or integrations.
- `medusa-plugin-storage`: For plugins that add a file service or storage integration.
- `medusa-plugin-source`: For plugins that help migrate or import data into Medusa from another platform.
- `medusa-plugin-storefront`: For storefronts that can be integrated with a Medusa backend.
- `medusa-plugin-other`: For any other type of plugin.
### Scripts in package.json
Make sure you add the `publish` command to your `scripts` field and make the following change to the `build` command:
```json title=package.json
"build": "babel src --out-dir . --ignore **/__tests__ --extensions \".ts,.js\"",
"prepare": "cross-env NODE_ENV=production npm run build"
```
The `build` command ensures that the plugin's built files are placed as explained in the [plugin structure](./create.md#plugin-structure) section of the Create Plugin documentation.
The `prepare` command facilitates your publishing process. You would typically run this script before publishing your plugin.
This new script requires installing the package `cross-env` as a development dependency:
```bash npm2yarn
npm install --save-dev cross-env
```
### Plugin Structure
Make sure your plugin's structure is as described in the [Create Plugin](./create.md#plugin-structure) documentation. If you've made the changes mentioned in [the above section to the scripts](#scripts-in-packagejson) in `package.json`, you should have the correct structure when you run the `prepare` command.
### NPM Ignore File
Not all files that you use while developing your plugin are necessary to be published.
For example, the files you add in the `src` directory are compiled to the root of the plugin directory before publishing. Then, when a developer installs your plugin, theyll just be using the files in the root.
So, you can ignore files and directories like `src` from the final published NPM package.
To do that, create the file `.npmignore` with the following content:
```bash title=.npmignore
/lib
node_modules
.DS_store
.env*
/*.js
!index.js
yarn.lock
src
.gitignore
.eslintrc
.babelrc
.prettierrc
# These are files that are included in a
# Medusa project and can be removed from a
# plugin project
medusa-config.js
Dockerfile
medusa-db.sql
develop.sh
```
---
## Publish Plugin
This section explains how to publish your plugin to NPM.
Before you publish a plugin, you must [create an account on NPM](https://www.npmjs.com/signup).
### Run Prepare Command
Before you publish or update your plugin, make sure to run the `prepare` command [defined earlier](#packagejson-checklist):
```bash npm2yarn
npm run prepare
```
### Login
In your terminal, log in with your NPM account:
```bash
npm login
```
Youll be asked to enter your NPM email and password.
### Publish Plugin Package
Once youre logged in, you can publish your package with the following command:
```bash
npm publish
```
Your package is then published on NPM and everyone can use it and install it.
### Install Plugin
To install your published plugin, you can run the following command on any Medusa backend project:
```bash npm2yarn
npm install medusa-plugin-custom
```
### Update Plugin
To update your plugin at a later point, you can run the following command to change the NPM version:
```bash
npm version <type>
```
Where `<type>` indicates the type of version update youre publishing. For example, it can be `major` or `minor`. You can see the [full list of types in NPMs documentation](https://docs.npmjs.com/cli/v8/commands/npm-version).
Then, publish the new update:
```bash
npm publish
```

View File

@@ -0,0 +1,771 @@
---
description: 'Learn how to implement publishable API key functionalities for admins in Medusa using the REST APIs. This includes how to list, create, update, and delete a publishable API key.'
addHowToData: true
---
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
# How to Manage Publishable API Keys
In this document, youll learn how to manage the publishable API keys using the admin APIs.
## Overview
Publishable API keys allow you to associate a set of resources with an API key. You can then pass that API key in storefront requests to guarantee that processed or returned data is within the scope you defined when creating the API key.
Currently, publishable API keys can only be associated with sales channels.
Using the Admin APIs, you can manage your Publishable API Keys.
:::note
Publishable API keys are only for client-side use. They can be publicly accessible in your code, as they are not authorized for the Admin API.
:::
### Scenario
You want to use or implement the following admin functionalities:
- Manage publishable keys including listing, creating, updating, and deleting them.
- Manage sales channels associated with a publishable API key including listing, adding, and deleting them from the publishable API key.
---
## Prerequisites
### Medusa Components
It is assumed that you already have a Medusa backend installed and set up. If not, you can follow the [quickstart guide](../../backend/install.mdx) to get started.
### JS Client
This guide includes code snippets to send requests to your Medusa backend using Medusas JS Client, among other methods.
If you follow the JS Client code blocks, its assumed you already have [Medusas JS Client](../../../js-client/overview.md) installed and have [created an instance of the client](../../../js-client/overview.md#configuration).
### Medusa React
This guide also includes code snippets to send requests to your Medusa backend using Medusa React, among other methods.
If you follow the Medusa React code blocks, it's assumed you already have [Medusa React installed](../../../medusa-react/overview.md) and have [used MedusaProvider higher in your component tree](../../../medusa-react/overview.md#usage).
### Authenticated Admin User
You must be an authenticated admin user before following along with the steps in the tutorial.
You can learn more about [authenticating as an admin user in the API reference](/api/admin/#section/Authentication).
---
## List Publishable API Keys
You can retrieve a list of publishable API keys by sending a request to the [List Publishable API Keys](/api/admin/#tag/PublishableApiKey/operation/GetPublishableApiKeys) endpoint:
<Tabs groupId="request-type" wrapperClassName="code-tabs">
<TabItem value="client" label="Medusa JS Client" default>
```ts
medusa.admin.publishableApiKeys.list()
.then(({ publishable_api_keys, count, limit, offset }) => {
console.log(publishable_api_keys)
})
```
</TabItem>
<TabItem value="medusa-react" label="Medusa React">
```tsx
import { PublishableApiKey } from "@medusajs/medusa"
import { useAdminPublishableApiKeys } from "medusa-react"
const PublishabelApiKeys = () => {
const { publishable_api_keys, isLoading } =
useAdminPublishableApiKeys()
return (
<div>
{isLoading && <span>Loading...</span>}
{publishable_api_keys && !publishable_api_keys.length && (
<span>No Publishable API Keys</span>
)}
{publishable_api_keys &&
publishable_api_keys.length > 0 && (
<ul>
{publishable_api_keys.map(
(publishableApiKey: PublishableApiKey) => (
<li key={publishableApiKey.id}>
{publishableApiKey.title}
</li>
)
)}
</ul>
)}
</div>
)
}
export default PublishabelApiKeys
```
</TabItem>
<TabItem value="fetch" label="Fetch API">
```ts
fetch(`<BACKEND_URL>/admin/publishable-api-keys`, {
credentials: "include",
})
.then((response) => response.json())
.then(({ publishable_api_keys, count, limit, offset }) => {
console.log(publishable_api_keys)
})
```
</TabItem>
<TabItem value="curl" label="cURL">
```bash
curl -L -X GET '<BACKEND_URL>/admin/publishable-api-keys' \
-H 'Authorization: Bearer <API_TOKEN>'
```
</TabItem>
</Tabs>
This request does not require any path parameters. You can pass it optional query parameters related to expanding fields and pagination, which you can check out in the [API reference](/api/admin/#tag/PublishableApiKey/operation/GetPublishableApiKeys).
This request returns the following data in the response:
- `publishable_api_keys`: An array of publishable API keys.
- `limit`: The maximum number of keys that can be returned.
- `offset`: The number of keys skipped in the result.
- `count`: The total number of keys available.
:::note
You can learn more about pagination in the [API reference](/api/admin/#section/Pagination).
:::
---
## Create a Publishable API Key
You can create a publishable API key by sending a request to the [Create Publishable API Key](/api/admin/#tag/PublishableApiKey/operation/PostPublishableApiKeys) endpoint:
<Tabs groupId="request-type" wrapperClassName="code-tabs">
<TabItem value="client" label="Medusa JS Client" default>
```ts
medusa.admin.publishableApiKeys.create({
title: "Web API Key",
})
.then(({ publishable_api_key }) => {
console.log(publishable_api_key.id)
})
```
</TabItem>
<TabItem value="medusa-react" label="Medusa React">
```tsx
import { useAdminCreatePublishableApiKey } from "medusa-react"
const CreatePublishableApiKey = () => {
const createKey = useAdminCreatePublishableApiKey()
// ...
const handleCreate = (title: string) => {
createKey.mutate({
title,
})
}
// ...
}
export default CreatePublishableApiKey
```
</TabItem>
<TabItem value="fetch" label="Fetch API">
```ts
fetch(`<BACKEND_URL>/admin/publishable-api-keys`, {
method: "POST",
credentials: "include",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
title: "Web API Key",
}),
})
.then((response) => response.json())
.then(({ publishable_api_key }) => {
console.log(publishable_api_key.id)
})
```
</TabItem>
<TabItem value="curl" label="cURL">
```bash
curl -L -X POST '<BACKEND_URL>/admin/publishable-api-keys' \
-H 'Authorization: Bearer <API_TOKEN>' \
-H 'Content-Type: application/json' \
--data-raw '{
"title": "Web API Key"
}'
```
</TabItem>
</Tabs>
This request requires a body parameter `title`, which is the title of the Publishable API Key.
It returns the created publishable API key in the response.
---
## Update a Publishable API Key
You can update a publishable API keys details by sending a request to the [Update Publishable API Key](/api/admin/#tag/PublishableApiKey/operation/PostPublishableApiKysPublishableApiKey) endpoint:
<Tabs groupId="request-type" wrapperClassName="code-tabs">
<TabItem value="client" label="Medusa JS Client" default>
```ts
medusa.admin.publishableApiKeys.update(publishableApiKeyId, {
title: "Web API Key",
})
.then(({ publishable_api_key }) => {
console.log(publishable_api_key.id)
})
```
</TabItem>
<TabItem value="medusa-react" label="Medusa React">
```tsx
import { useAdminUpdatePublishableApiKey } from "medusa-react"
const UpdatePublishableApiKey = () => {
const updateKey = useAdminUpdatePublishableApiKey(
publishableApiKeyId
)
// ...
const handleUpdate = (title: string) => {
updateKey.mutate({
title,
})
}
// ...
}
export default UpdatePublishableApiKey
```
</TabItem>
<TabItem value="fetch" label="Fetch API">
<!-- eslint-disable max-len -->
```ts
fetch(`<BACKEND_URL>/admin/publishable-api-keys/${publishableApiKeyId}`, {
method: "POST",
credentials: "include",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
title: "Web API Key",
}),
})
.then((response) => response.json())
.then(({ publishable_api_key }) => {
console.log(publishable_api_key.id)
})
```
</TabItem>
<TabItem value="curl" label="cURL">
```bash
curl -L -X POST '<BACKEND_URL>/admin/publishable-api-keys/<PUBLISHABLE_API_KEY>' \
-H 'Authorization: Bearer <API_TOKEN>' \
-H 'Content-Type: application/json' \
--data-raw '{
"title": "Web API Key"
}'
```
</TabItem>
</Tabs>
This request requires the ID of the publishable API key as a path parameter. In its body, it optionally accepts the new `title` of the publishable API key.
This request returns the update publishable API key object in the response.
---
## Revoke a Publishable API Key
Revoking a publishable API key does not remove it, but does not allow using it in future requests.
You can revoke a publishable API key by sending a request to the [Revoke Publishable API Key](/api/admin/#tag/PublishableApiKey/operation/PostPublishableApiKeysPublishableApiKeyRevoke) endpoint:
<Tabs groupId="request-type" wrapperClassName="code-tabs">
<TabItem value="client" label="Medusa JS Client" default>
```ts
medusa.admin.publishableApiKeys.revoke(publishableApiKeyId)
.then(({ publishable_api_key }) => {
console.log(publishable_api_key.id)
})
```
</TabItem>
<TabItem value="medusa-react" label="Medusa React">
```tsx
import { useAdminRevokePublishableApiKey } from "medusa-react"
const PublishableApiKey = () => {
const revokeKey = useAdminRevokePublishableApiKey(
publishableApiKeyId
)
// ...
const handleRevoke = () => {
revokeKey.mutate()
}
// ...
}
export default PublishableApiKey
```
</TabItem>
<TabItem value="fetch" label="Fetch API">
<!-- eslint-disable max-len -->
```ts
fetch(
`<BACKEND_URL>/admin/publishable-api-keys/${publishableApiKeyId}/revoke`,
{
method: "POST",
credentials: "include",
}
)
.then((response) => response.json())
.then(({ publishable_api_key }) => {
console.log(publishable_api_key.id)
})
```
</TabItem>
<TabItem value="curl" label="cURL">
```bash
curl -L -X POST '<BACKEND_URL>/admin/publishable-api-keys/<PUBLISHABLE_API_KEY>/revoke' \
-H 'Authorization: Bearer <API_TOKEN>'
```
</TabItem>
</Tabs>
This request requires the ID of the publishable API key as a path parameter. It returns the updated publishable API key in the response.
---
## Delete a Publishable API Key
You can delete a publishable API key by sending a request to the [Delete Publishable API Key](/api/admin/#tag/PublishableApiKey/operation/DeletePublishableApiKeysPublishableApiKey) endpoint:
<Tabs groupId="request-type" wrapperClassName="code-tabs">
<TabItem value="client" label="Medusa JS Client" default>
```ts
medusa.admin.publishableApiKeys.delete(publishableApiKeyId)
.then(({ id, object, deleted }) => {
console.log(id)
})
```
</TabItem>
<TabItem value="medusa-react" label="Medusa React">
```tsx
import { useAdminDeletePublishableApiKey } from "medusa-react"
const PublishableApiKey = () => {
const deleteKey = useAdminDeletePublishableApiKey(
publishableApiKeyId
)
// ...
const handleDelete = () => {
deleteKey.mutate()
}
// ...
}
export default PublishableApiKey
```
</TabItem>
<TabItem value="fetch" label="Fetch API">
<!-- eslint-disable max-len -->
```ts
fetch(`<BACKEND_URL>/admin/publishable-api-keys/${publishableApiKeyId}`, {
method: "DELETE",
credentials: "include",
})
.then((response) => response.json())
.then(({ id, object, deleted }) => {
console.log(id)
})
```
</TabItem>
<TabItem value="curl" label="cURL">
```bash
curl -L -X DELETE '<BACKEND_URL>/admin/publishable-api-keys/<PUBLISHABLE_API_KEY>' \
-H 'Authorization: Bearer <API_TOKEN>'
```
</TabItem>
</Tabs>
This request requires the ID of the publishable API key as a path parameter.
It returns the following data in the response:
- `id`: The ID of the deleted publishable API key.
- `object`: A string indicating the type of object deleted. By default, its value is `publishable_api_key`.
- `deleted`: A boolean value indicating whether the publishable API key was deleted or not.
---
## Manage Sales Channels of Publishable API Keys
This section covers how to manage sales channels in a publishable API key. This doesnt affect sales channels and their data, only its association with the publishable API key.
### List Sales Channels of a Publishable API Key
You can retrieve the list of sales channels associated with a publishable API key by sending a request to the [List Sales Channels](/api/admin/#tag/PublishableApiKey/operation/GetPublishableApiKeySalesChannels) endpoint:
<Tabs groupId="request-type" wrapperClassName="code-tabs">
<TabItem value="client" label="Medusa JS Client" default>
```ts
medusa.admin.publishableApiKeys
.listSalesChannels(publishableApiKeyId)
.then(({ sales_channels }) => {
console.log(sales_channels)
})
```
</TabItem>
<TabItem value="medusa-react" label="Medusa React">
```tsx
import { SalesChannel } from "@medusajs/medusa"
import {
useAdminPublishableApiKeySalesChannels,
} from "medusa-react"
const SalesChannels = () => {
const { sales_channels, isLoading } =
useAdminPublishableApiKeySalesChannels(
publishableApiKeyId
)
return (
<div>
{isLoading && <span>Loading...</span>}
{sales_channels && !sales_channels.length && (
<span>No Sales Channels</span>
)}
{sales_channels && sales_channels.length > 0 && (
<ul>
{sales_channels.map((salesChannel: SalesChannel) => (
<li key={salesChannel.id}>{salesChannel.name}</li>
))}
</ul>
)}
</div>
)
}
export default SalesChannels
```
</TabItem>
<TabItem value="fetch" label="Fetch API">
<!-- eslint-disable max-len -->
```ts
fetch(
`<BACKEND_URL>/admin/publishable-api-keys/${publishableApiKeyId}/sales-channels`,
{
credentials: "include",
}
)
.then((response) => response.json())
.then(({ sales_channels }) => {
console.log(sales_channels)
})
```
</TabItem>
<TabItem value="curl" label="cURL">
```bash
curl -L -X GET '<BACKEND_URL>/admin/publishable-api-keys/<PUBLISHABLE_API_KEY>/sales-channels' \
-H 'Authorization: Bearer <API_TOKEN>'
```
</TabItem>
</Tabs>
This request requires the ID of the publishable API key as a path parameter.
It returns an array of sales channels associated with the publishable API key in the response.
### Add Sales Channels to Publishable API Key
You can add a sales channel to a publishable API key by sending a request to the [Add Sales Channels](/api/admin/#tag/PublishableApiKey/operation/PostPublishableApiKeySalesChannelsChannelsBatch) endpoint:
<Tabs groupId="request-type" wrapperClassName="code-tabs">
<TabItem value="client" label="Medusa JS Client" default>
```ts
medusa.admin.publishableApiKeys.addSalesChannelsBatch(
publishableApiKeyId,
{
sales_channel_ids: [
{
id: salesChannelId,
},
],
}
)
.then(({ publishable_api_key }) => {
console.log(publishable_api_key.id)
})
```
</TabItem>
<TabItem value="medusa-react" label="Medusa React">
```tsx
import {
useAdminAddPublishableKeySalesChannelsBatch,
} from "medusa-react"
const PublishableApiKey = () => {
const addSalesChannels =
useAdminAddPublishableKeySalesChannelsBatch(
publishableApiKeyId
)
// ...
const handleAdd = (salesChannelId: string) => {
addSalesChannels.mutate({
sales_channel_ids: [
{
id: salesChannelId,
},
],
})
}
// ...
}
export default PublishableApiKey
```
</TabItem>
<TabItem value="fetch" label="Fetch API">
<!-- eslint-disable max-len -->
```ts
fetch(
`<BACKEND_URL>/admin/publishable-api-keys/${publishableApiKeyId}/sales-channels/batch`,
{
method: "POST",
credentials: "include",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
sales_channel_ids: [
{
id: salesChannelId,
},
],
}),
}
)
.then((response) => response.json())
.then(({ publishable_api_key }) => {
console.log(publishable_api_key.id)
})
```
</TabItem>
<TabItem value="curl" label="cURL">
```bash
curl -L -X POST '<BACKEND_URL>/admin/publishable-api-keys/<PUBLISHABLE_API_KEY>/sales-channels/batch' \
-H 'Authorization: Bearer <API_TOKEN>' \
-H 'Content-Type: application/json' \
--data-raw '{
"sales_channel_ids": [
{
"id": "<SALES_CHANNEL_ID>"
}
]
}'
```
</TabItem>
</Tabs>
This request requires passing the ID of the publishable API key as a path parameter.
In its body parameters, its required to pass the `sales_channel_ids` array parameter. Each item in the array is an object containing an `id` property, with its value being the ID of the sales channel to add to the publishable API key.
You can add more than one sales channel in the same request by passing each of them as an object in the array.
This request returns the updated publishable API key in the response.
### Delete Sales Channels from a Publishable API Key
You can delete a sales channel from a publishable API key by sending a request to the [Delete Sales Channels](/api/admin/#tag/PublishableApiKey/operation/DeletePublishableApiKeySalesChannelsChannelsBatch) endpoint:
<Tabs groupId="request-type" wrapperClassName="code-tabs">
<TabItem value="client" label="Medusa JS Client" default>
```ts
medusa.admin.publishableApiKeys.deleteSalesChannelsBatch(
publishableApiKeyId,
{
sales_channel_ids: [
{
id: salesChannelId,
},
],
}
)
.then(({ publishable_api_key }) => {
console.log(publishable_api_key.id)
})
```
</TabItem>
<TabItem value="medusa-react" label="Medusa React">
```tsx
import {
useAdminRemovePublishableKeySalesChannelsBatch,
} from "medusa-react"
const PublishableApiKey = () => {
const deleteSalesChannels =
useAdminRemovePublishableKeySalesChannelsBatch(
publishableApiKeyId
)
// ...
const handleDelete = (salesChannelId: string) => {
deleteSalesChannels.mutate({
sales_channel_ids: [
{
id: salesChannelId,
},
],
})
}
// ...
}
export default PublishableApiKey
```
</TabItem>
<TabItem value="fetch" label="Fetch API">
<!-- eslint-disable max-len -->
```ts
fetch(
`<BACKEND_URL>/admin/publishable-api-keys/${publishableApiKeyId}/sales-channels/batch`,
{
method: "DELETE",
credentials: "include",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
sales_channel_ids: [
{
id: salesChannelId,
},
],
}),
}
)
.then((response) => response.json())
.then(({ publishable_api_key }) => {
console.log(publishable_api_key.id)
})
```
</TabItem>
<TabItem value="curl" label="cURL">
```bash
curl -L -X DELETE '<BACKEND_URL>/admin/publishable-api-keys/<PUBLISHABLE_API_KEY>/sales-channels/batch' \
-H 'Authorization: Bearer <API_TOKEN>' \
-H 'Content-Type: application/json' \
--data-raw '{
"sales_channel_ids": [
{
"id": "<SALES_CHANNEL_ID>"
}
]
}'
```
</TabItem>
</Tabs>
This request requires the ID of the publishable API key as a path parameter.
In its body parameters, its required to pass the `sales_channel_ids` array parameter. Each item in the array is an object containing an `id` property, with its value being the ID of the sales channel to delete from the publishable API key.
You can delete more than one sales channel in the same request by passing each of them as an object in the array.
This request returns the updated publishable API key in the response.
---
## See Also
- [Use publishable API keys in client requests](../storefront/use-in-requests.md)

View File

@@ -0,0 +1,69 @@
---
description: 'Learn what publishable API keys are and how they can be used in the Medusa backend. Publishable API keys can be used to scope API calls with an API key.'
---
import DocCardList from '@theme/DocCardList';
import Icons from '@theme/Icon';
# Publishable API Keys
In this document, youll learn about Publishable API Keys and their architecture.
## Introduction
While using Medusas APIs, you might have to pass some query parameters for certain resources with every or most requests.
Taking Sales Channels as an example, you have to pass the Sales Channels ID as a query parameter to all the necessary endpoints, such as the List Products endpoint.
This is a tedious and error-prone process. This is where Publishable API Keys are useful.
Publishable API Keys can be used to scope API calls with an API key, determining what resources are retrieved when querying the API. Currently, they can be associated only with Sales Channels.
For example, you can associate an API key with a B2B channel, then, on the storefront, retrieve only products available in that channel using the API key.
---
## PublishableApiKey Entity Overview
The `PublishableApiKey` entity represents a publishable API key that is stored in the database. Some of its important attributes include:
- `id`: The ID of the publishable API key. This is the API key youll use in your API requests.
- `created_by`: The ID of the user that created this API key.
- `revoked_by`: The ID of the user that revoked this API key. A revoked publishable API key cannot be used in requests.
---
## Relation to Other Entities
### Sales Channels
A publishable API key can be associated with more than one sales channel, and a sales channel can be associated with more than one publishable API key.
The relation is represented by the entity `PublishableApiKeySalesChannel`.
---
## Custom Development
Developers can manage Publishable API Keys and use them when making requests to the Store APIs.
<DocCardList colSize={6} items={[
{
type: 'link',
href: '/development/publishable-api-keys/admin/manage-publishable-api-keys',
label: 'Admin: Manage Publishable API Keys',
customProps: {
icon: Icons['academic-cap-solid'],
description: 'Learn how to manage publishable API keys using Admin APIs.'
}
},
{
type: 'link',
href: '/development/publishable-api-keys/storefront/use-in-requests',
label: 'Storefront: Use in Requests',
customProps: {
icon: Icons['academic-cap-solid'],
description: 'Learn how to use publishable API keys in a storefront.'
}
},
]} />

View File

@@ -0,0 +1,78 @@
---
description: 'Learn how to use Publishable API Keys in Client Requests using Medusa JS Client, Medusa React, or other methods.'
---
# Use Publishable API Keys in Client Requests
In this document, you'll learn how to use Publishable API Keys in client requests.
:::note
[Publishable API keys](../index.mdx) are only for client-side use. They can be publicly accessible in your code, as they are not authorized for the Admin API.
:::
## Using Medusa JS Client
When using [Medusas JS Client](../../../js-client/overview.md), you can pass it to the client only once when you create the instance of the client:
```ts
const medusa = new Medusa({
maxRetries: 3,
baseUrl: "https://api.example.com",
publishableApiKey,
})
```
This will add the API key as in the header parameter `x-publishable-api-key` on all requests.
You can also use the `setPublishableKey` method to set it at a later point:
```ts
const medusa = new Medusa({
// ...
})
// at a later point
medusa.setPublishableKey(publishableApiKey)
```
## Using Medusa React
You can pass the publishable API key to the `MedusaProvider` component:
```tsx
const App = () => {
return (
<MedusaProvider
queryClientProviderProps={{ client: queryClient }}
baseUrl="http://localhost:9000"
// ...
publishableApiKey={publishableApiKey}
>
<MyStorefront />
</MedusaProvider>
)
}
```
Then, the API key will be passed in the header parameter `x-publishable-api-key` of every request.
## Using Other Methods
For other ways of sending requests to your Medusa backend, such as using the Fetch API, you must pass `x-publishable-api-key` in the header of every request. Its value is the publishable API keys `id`.
```ts
fetch(`<BACKEND_URL>/store/products`, {
credentials: "include",
headers: {
"x-publishable-api-key": publishableApiKey,
},
})
```
---
## See Also
- [Manage publishable keys as an admin](../admin/manage-publishable-api-keys.mdx)

View File

@@ -0,0 +1,155 @@
---
description: 'Learn how to create a scheduled job in Medusa. The scheduled job in this example will simply change the status of draft products to published.'
addHowToData: true
---
# How to Create a Scheduled Job
In this document, youll learn how to create a scheduled job in Medusa.
## Overview
Medusa allows you to create scheduled jobs that run at specific times during your backend's lifetime. For example, you can synchronize your inventory with an Enterprise Resource Planning (ERP) system once a day.
This guide explains how to create a scheduled job on your Medusa backend. The scheduled job in this example will simply change the status of draft products to `published`.
---
## Prerequisites
### Medusa Components
It is assumed that you already have a Medusa backend installed and set up. If not, you can follow the [quickstart guide](../backend/install.mdx) to get started.
### Redis
Redis is required for scheduled jobs to work. Make sure you [install Redis](../../development/backend/prepare-environment.mdx#redis) and [configure it with your Medusa backend](../../development/backend/configurations.md#redis).
---
## 1. Create a File
Each scheduled job should reside in a TypeScript or JavaScript file under the `src/loaders` directory.
Start by creating the `src/loaders` directory. Then, inside that directory, create the JavaScript or TypeScript file that youll add the scheduled job in. You can use any name for the file.
For the example in this tutorial, you can create the file `src/loaders/publish.ts`.
---
## 2. Create Cron Job
To create a scheduled job, add the following code in the file you created, which is `src/loaders/publish.ts` in this example:
```ts title=src/loaders/publish.ts
const publishJob = async (container, options) => {
const jobSchedulerService =
container.resolve("jobSchedulerService")
jobSchedulerService.create(
"publish-products",
{},
"0 0 * * *",
async () => {
// job to execute
const productService = container.resolve("productService")
const draftProducts = await productService.list({
status: "draft",
})
for (const product of draftProducts) {
await productService.update(product.id, {
status: "published",
})
}
}
)
}
export default publishJob
```
:::info
The service taking care of background jobs was renamed in v1.7.1. If you are running a previous version, use `eventBusService` instead of `jobSchedulerService`.
:::
This file should export a function that accepts a `container` and `options` parameters. `container` is the dependency container that you can use to resolve services, such as the JobSchedulerService. `options` are the plugins options if this scheduled job is created in a plugin.
You then resolve the `JobSchedulerService` and use the `jobSchedulerService.create` method to create the scheduled job. This method accepts four parameters:
- The first parameter is a unique name to give to the scheduled job. In the example above, you use the name `publish-products`;
- The second parameter is an object which can be used to [pass data to the job](#pass-data-to-the-scheduled-job);
- The third parameter is the scheduled job expression pattern. In this example, it will execute the scheduled job once a day at 12 AM.
- The fourth parameter is the function to execute. This is where you add the code to execute once the scheduled job runs. In this example, you retrieve the draft products using the [ProductService](../../references/services/classes/ProductService.md) and update the status of each of these products to `published`.
:::tip
You can see examples of scheduled job expression patterns on [crontab guru](https://crontab.guru/examples.html).
:::
### Pass Data to the Cron Job
To pass data to your scheduled job, you can add them to the object passed as a second parameter under the `data` property. This is helpful if you use one function to handle multiple scheduled jobs.
For example:
```ts
jobSchedulerService.create("publish-products", {
data: {
productId,
},
}, "0 0 * * *", async (job) => {
console.log(job.data) // {productId: 'prod_124...'}
// ...
})
```
---
## 3. Run Medusa Backend
:::info
Cron Jobs only run while the Medusa backend is running.
:::
In your terminal run the following command to run your Medusa backend:
```bash npm2yarn
npm run start
```
This builds your code under the `src` directory into the `dist` directory, then runs the Medusa backend.
If the scheduled job was registered successfully, you should see a message similar to this logged on your Medusa backend:
```bash
Registering publish-products
```
Where `publish-products` is the unique name you provided to the scheduled job.
Once it is time to run your scheduled job based on the scheduled job expression pattern, the scheduled job will run and you can see it logged on your Medusa backend.
For example, the above scheduled job will run at 12 AM and, when it runs, you can see the following logged on your Medusa backend:
```bash noReport
info: Processing scheduled job: publish-products
```
If you log anything in the scheduled job, for example using `console.log`, or if any errors are thrown, itll also be logged on your Medusa backend.
:::tip
To test the previous example out instantly, you can change the scheduled job expression pattern passed as the third parameter to `jobSchedulerService.create` to `* * * * *`. This will run the scheduled job every minute.
:::
---
## See Also
- [Create a Plugin](../plugins/create.md)

View File

@@ -0,0 +1,40 @@
---
description: "Learn what scheduled jobs are in Medusa. Scheduled jobs (also known as cron jobs) are tasks performed at a specific time in the Medusa Backend."
---
import DocCard from '@theme/DocCard';
import Icons from '@theme/Icon';
# Scheduled Jobs
In this document, youll learn what scheduled jobs are in Medusa.
## Introduction
Scheduled jobs (also known as cron jobs) are tasks performed at a specific time while the Medusa Backend is running. Theyre used to perform asynchronous tasks in the background.
For example, you can synchronize your inventory with an Enterprise Resource Planning (ERP) system once a day using a scheduled job.
In the Medusa Backend, the scheduled jobs queue is implemented using [Redis](https://redis.io/) and [Bull](https://www.npmjs.com/package/bull). So, for scheduled jobs to work, you must have [Redis enabled](../../development/backend/configurations.md#redis).
:::tip
Future versions of Medusa will allow switching out Redis and using a different pub/sub service.
:::
---
## Custom Development
Developers can create an unlimited number of scheduled jobs within the Medusa Backend, a plugin, or a custom Commerce Module.
<DocCard item={{
type: 'link',
href: '/development/scheduled-jobs/create',
label: 'Create a Scheduled Job',
customProps: {
icon: Icons['academic-cap-solid'],
description: 'Learn how to create a scheduled job.'
}
}} />

View File

@@ -0,0 +1,121 @@
---
description: 'Learn how to create a service in Medusa. This guide also includes how to use services in other services, subscribers, and endpoints.'
addHowToData: true
---
# Create a Service
In this document, youll learn how you can create a [Service](./overview.mdx) and use it across your Medusa backend just like any of the core services.
## Implementation
To create a service, create a TypeScript or JavaScript file in `src/services` to hold the service. The name of the file should be the registration name of the service without `Service` as it will be appended to it by default.
For example, if you want to create a service `helloService`, create the file `hello.ts` in `src/services` with the following content:
```ts title=/src/services/hello.ts
import { TransactionBaseService } from "@medusajs/medusa"
import { EntityManager } from "typeorm"
class HelloService extends TransactionBaseService {
protected manager_: EntityManager
protected transactionManager_: EntityManager
getMessage() {
return `Welcome to My Store!`
}
}
export default HelloService
```
---
## Service Constructor
As the service extends the `TransactionBaseService` class, all services in Medusas core, as well as all your custom services, will be available in your services constructor using dependency injection.
So, if you want your service to use another service, simply add it as part of your constructors dependencies and set it to a field inside your services class:
```ts
class HelloService extends TransactionBaseService {
private productService: ProductService
constructor(container) {
super(container)
this.productService = container.productService
}
// ...
}
```
Then, you can use that service anywhere in your custom service:
```ts
class HelloService extends TransactionBaseService {
// ...
async getProductCount() {
return await this.productService.count()
}
}
```
---
## Use a Service
In this section, you'll learn how to use services throughout your Medusa backend. This includes both Medusa's services and your custom services.
:::note
Before using your service, make sure you run the `build` command:
```bash npm2yarn
npm run build
```
:::
### In a Service
To use your custom service in another custom service, you can have easy access to it in the dependencies injected to the constructor of your service:
```ts
class MyService extends TransactionBaseService {
constructor(container) {
super(container)
this.helloService = container.helloService
}
// ...
}
```
### In an Endpoint
To use your custom service in an endpoint, you can use `req.scope.resolve` passing it the services registration name:
```ts
const helloService = req.scope.resolve("helloService")
res.json({
message: helloService.getMessage(),
})
```
### In a Subscriber
To use your custom service in a subscriber, you can have easy access to it in the subscribers dependencies injected to the constructor of your subscriber:
```ts
class MySubscriber {
constructor({ helloService, eventBusService }) {
this.helloService = helloService
}
// ...
}
```
---
## See Also
- [Create a Plugin](../plugins/create.md)

View File

@@ -0,0 +1,49 @@
---
description: 'Learn what Services are in Medusa. Services represent bundled helper methods that you want to use across your commerce application.'
---
import DocCard from '@theme/DocCard';
import Icons from '@theme/Icon';
# Services
In this document, you'll learn about what Services are in Medusa.
## What are Services
Services in Medusa represent bundled helper methods that you want to use across your commerce application. By convention, they represent a certain entity or functionality in Medusa.
For example, you can use Medusas `productService` to get the list of products, as well as perform other functionalities related to products. Theres also an `authService` that provides functionalities like authenticating customers and users.
In the Medusa backend, custom services are TypeScript or JavaScript files located in the `src/services` directory. Each service should be a class that extends the `TransactionBaseService` class from the core Medusa package `@medusajs/medusa`. Each file you create in `src/services` should hold one service and export it.
The file name is important as it determines the name of the service when you need to use it elsewhere. The name of the service will be registered as the camel-case version of the file name + `Service` at the end of the name.
For example, if the file name is `hello.ts`, the service will be registered as `helloService`. If the file name is `hello-world.ts`, the service name will be registered as `helloWorldService`.
The registration name of the service is important, as youll be referring to it when you want to get access to the service using dependency injection or in routes.
The service must then be transpiled using the `build` command, which moves them to the `dist` directory, to be used across your commerce application.
:::tip
If you're creating a service in a plugin, learn more about the required structure [here](../plugins/create.md#plugin-structure).
:::
---
## Custom Development
Developers can create custom services in the Medusa backend, a plugin, or in a Commerce Module.
<DocCard item={{
type: 'link',
href: '/development/services/create-service',
label: 'Create a Service',
customProps: {
icon: Icons['academic-cap-solid'],
description: 'Learn how to create a service in Medusa.'
}
}}
/>

View File

@@ -0,0 +1,68 @@
---
description: "Learn what a Strategy is in Medusa. A strategy is an isolated piece of business logic that can be overridden and customized."
---
import DocCardList from '@theme/DocCardList';
import Icons from '@theme/Icon';
# Strategy
In this document, youll learn what a Strategy is in Medusa.
## Introduction
A strategy is an isolated piece of business logic that can be overridden and customized. Its a TypeScript or JavaScript class that doesnt have to follow a specific definition, but performs only a single functionality.
For example, in the core `@medusajs/medusa` package, strategies are used to implement functionalities like cart completion and product import.
These strategy classes are then resolved in endpoints, services, or wherever needed using dependency injection and used to perform their designated functionality.
For example, the `CartCompletionStrategy` is resolved in the Complete Cart endpoint that is defined in the core `@medusajs/medusa` package. Its then used to complete the cart and place the order:
```ts
export default async (req, res) => {
// ...
const completionStrat: AbstractCartCompletionStrategy =
req.scope.resolve(
"cartCompletionStrategy"
)
const {
response_code,
response_body,
} = await completionStrat.complete(
id,
idempotencyKey,
req.request_context
)
res.status(response_code).json(response_body)
}
```
Developers can override these strategies defined in the core to customize their functionality. Dependency injection then resolves the strategy (in the code example above, `CartCompletionStrategy`) to the custom strategy that the developer created. Then, the method (in the code example above, `complete`) of the custom strategy will be executed instead of the one defined in the core package.
## Custom Development
<DocCardList colSize={6} items={[
{
type: 'link',
href: '#',
label: 'Backend: Create a Strategy',
customProps: {
icon: Icons['academic-cap-solid'],
description: 'Learn how to create a strategy.',
isSoon: true
}
},
{
type: 'link',
href: '#',
label: 'Backend: Override a Strategy',
customProps: {
icon: Icons['academic-cap-solid'],
description: 'Learn how to override a strategy.',
isSoon: true
}
},
]} />