docs: general updates to documentation pages (#13055)

This commit is contained in:
Shahed Nasser
2025-07-28 10:52:22 +03:00
committed by GitHub
parent 6e66e36d08
commit cd599e1f62
10 changed files with 234 additions and 138 deletions

View File

@@ -6,13 +6,13 @@ export const metadata = {
# {metadata.title}
In this chapter, youll learn more about widgets and how to use them.
In this chapter, youll learn about widgets and how to use them.
## What is an Admin Widget?
The Medusa Admin dashboard's pages are customizable to insert widgets of custom content in pre-defined injection zones. You create these widgets as React components that allow admin users to perform custom actions.
The Medusa Admin's pages are customizable for inserting widgets of custom content in pre-defined injection zones. For example, you can add a widget on the product details page that allows admin users to sync products to a third-party service.
For example, you can add a widget on the product details page that allow admin users to sync products to a third-party service.
You create these widgets as React components that render the content and functionality of the widget.
---
@@ -25,7 +25,10 @@ For example, you can add a widget on the product details page that allow admin u
}]}
/>
You create a widget in a `.tsx` file under the `src/admin/widgets` directory. The files default export must be the widget, which is the React component that renders the custom content. The file must also export the widgets configurations indicating where to insert the widget.
You create a widget in a `.tsx` file under the `src/admin/widgets` directory. The file must export:
1. A React component that renders the widget. This will be the file's default export.
2. The widgets configurations indicating where to insert the widget.
For example, create the file `src/admin/widgets/product-widget.tsx` with the following content:
@@ -33,7 +36,7 @@ For example, create the file `src/admin/widgets/product-widget.tsx` with the fol
export const widgetHighlights = [
["5", "ProductWidget", "The React component of the product widget."],
["17", "zone", "The zone to inject the widget to."]
["17", "zone", "The zone to inject the widget into."]
]
```tsx title="src/admin/widgets/product-widget.tsx" highlights={widgetHighlights}
@@ -59,12 +62,12 @@ export const config = defineWidgetConfig({
export default ProductWidget
```
You export the `ProductWidget` component, which shows the heading `Product Widget`. In the widget, you use [Medusa UI](!ui!), a package that Medusa maintains to allow you to customize the dashboard with the same components used to build it.
To export the widget's configurations, you use `defineWidgetConfig` from the Admin Extension SDK. It accepts as a parameter an object with the `zone` property, whose value is a string or an array of strings, each being the name of the zone to inject the widget into.
In the example above, the widget is injected at the top of a products details.
You export the `ProductWidget` component, which displays the heading `Product Widget`. In the widget, you use [Medusa UI](!ui!) to customize the dashboard with the same components used to build it.
To export the widget's configuration, you use `defineWidgetConfig` from the Admin Extension SDK. It accepts an object as a parameter with the `zone` property, whose value is a string or an array of strings, each being the name of the zone to inject the widget into.
<Note title="Important" type="warning">
The widget component must be created as an arrow function.
@@ -83,9 +86,9 @@ Then, open a products details page. Youll find your custom widget at the t
---
## Props Passed in Detail Pages
## Props Passed to Widgets on Detail Pages
Widgets that are injected into a details page receive a `data` prop, which is the main data of the details page.
Widgets that are injected into a detail page receive a `data` prop, which is the main data of the details page.
For example, a widget injected into the `product.details.before` zone receives the product's details in the `data` prop:
@@ -130,15 +133,17 @@ The props type is `DetailWidgetProps`, and it accepts as a type argument the exp
---
## Injection Zone
## Injection Zones List
Refer to [this reference](!resources!/admin-widget-injection-zones) for the full list of injection zones and their props.
Refer to the [Admin Widget Injection Zones](!resources!/admin-widget-injection-zones) reference for the full list of injection zones and their props.
---
## Admin Components List
To build admin customizations that match the Medusa Admin's designs and layouts, refer to [this guide](!resources!/admin-components) to find common components.
While the Medusa Admin uses the [Medusa UI](!ui!) components, it also expands on them for styling and design purposes.
To build admin customizations that match the Medusa Admin's designs and layouts, refer to the [Admin Components](!resources!/admin-components) guide. You'll find components like `Header`, `JSON View`, and more that match the Medusa Admin's design.
---
@@ -146,7 +151,7 @@ To build admin customizations that match the Medusa Admin's designs and layouts,
In some cases, you may want to show a widget only if certain conditions are met. For example, you may want to show a widget only if the product has a brand.
To disable the widget from showing, return an empty fragment from the widget component:
To prevent the widget from showing, return an empty fragment from the widget component:
```tsx title="src/admin/widgets/product-widget.tsx"
import { defineWidgetConfig } from "@medusajs/admin-sdk"

View File

@@ -8,7 +8,7 @@ In this chapter, you'll learn about how to add new API routes for each HTTP meth
## HTTP Method Handler
An API route is created for every HTTP method you export a handler function for in a route file.
An API route is created for every HTTP method you export a handler function for in a `route.ts` or `route.js` file.
Allowed HTTP methods are: `GET`, `POST`, `DELETE`, `PUT`, `PATCH`, `OPTIONS`, and `HEAD`.
@@ -39,7 +39,7 @@ export const POST = async (
}
```
This adds two API Routes:
This file adds two API Routes:
- A `GET` route at `http://localhost:9000/hello-world`.
- A `POST` route at `http://localhost:9000/hello-world`.
- A `GET` API route at `http://localhost:9000/hello-world`.
- A `POST` API route at `http://localhost:9000/hello-world`.

View File

@@ -4,25 +4,25 @@ export const metadata = {
# {metadata.title}
In this chapter, youll learn what API Routes are and how to create them.
In this chapter, youll learn what API routes are and how to create them.
## What is an API Route?
An API Route is an endpoint. It exposes commerce features to external applications, such as storefronts, the admin dashboard, or third-party systems.
An API route is a REST endpoint that exposes commerce features to external applications, such as storefronts, the admin dashboard, or third-party systems.
The Medusa core application provides a set of admin and store API routes out-of-the-box. You can also create custom API routes to expose your custom functionalities.
The Medusa core application provides a set of [admin](!api!/admin) and [store](!api!/store) API routes out-of-the-box. You can also create custom API routes to expose your custom functionalities.
---
## How to Create an API Route?
An API Route is created in a TypeScript or JavaScript file under the `src/api` directory of your Medusa application. The files name must be `route.ts` or `route.js`.
You can create an API route in a TypeScript or JavaScript file under the `src/api` directory of your Medusa application. The files name must be `route.ts` or `route.js`.
![Example of API route in the application's directory structure](https://res.cloudinary.com/dza7lstvk/image/upload/v1732808645/Medusa%20Book/route-dir-overview_dqgzmk.jpg)
Each file exports API Route handler functions for at least one HTTP method (`GET`, `POST`, `DELETE`, etc…).
Each file exports API route handler functions for at least one HTTP method (`GET`, `POST`, `DELETE`, etc…).
For example, to create a `GET` API Route at `/hello-world`, create the file `src/api/hello-world/route.ts` with the following content:
For example, to create a `GET` API route at `/hello-world`, create the file `src/api/hello-world/route.ts` with the following content:
```ts title="src/api/hello-world/route.ts"
import type {
@@ -40,7 +40,7 @@ export const GET = (
}
```
### Test API Route
### Test the API Route
To test the API route above, start the Medusa application:
@@ -48,18 +48,22 @@ To test the API route above, start the Medusa application:
npm run dev
```
Then, send a `GET` request to the `/hello-world` API Route:
Then, send a `GET` request to the `/hello-world` API route:
```bash
curl http://localhost:9000/hello-world
```
You should receive the following response:
```json
{
"message": "[GET] Hello world!"
}
```
---
## When to Use API Routes
## Next Chapters: Learn More About API Routes
<Note title="Use API routes when" type="success">
You're exposing custom functionality to be used by a storefront, admin dashboard, or any external application.
</Note>
Follow the next chapters to learn about the different HTTP methods you can use in API routes, parameters and validation, protecting routes, and more.

View File

@@ -8,7 +8,7 @@ In this chapter, you'll learn how to send a response in your API route.
## Send a JSON Response
To send a JSON response, use the `json` method of the `MedusaResponse` object passed as the second parameter of your API route handler.
To send a JSON response, use the `json` method of the `MedusaResponse` object that is passed as the second parameter of your API route handler.
For example:
@@ -78,7 +78,7 @@ export const streamHighlights = [
["7", "writeHead", "Set the response's headers."],
["7", "200", "Set the status code."],
["8", `"Content-Type"`, "Set the response's content type."],
["13", "interval", "Simulate stream data using an interval"],
["13", "interval", "Simulate stream data using an interval."],
["14", "write", "Write stream data."],
["17", "on", "Stop the stream when the request is terminated."]
]
@@ -109,10 +109,10 @@ export const GET = async (
The `writeHead` method accepts two parameters:
1. The first one is the response's status code.
2. The second is an object of key-value pairs to set the headers of the response.
1. The first parameter is the response's status code.
2. The second parameter is an object of key-value pairs to set the response headers.
This API route opens a stream by setting the `Content-Type` in the header to `text/event-stream`. It then simulates a stream by creating an interval that writes the stream data every three seconds.
This API route opens a stream by setting the `Content-Type` to `text/event-stream`. It then simulates a stream by creating an interval that writes the stream data every three seconds.
---

View File

@@ -4,17 +4,22 @@ export const metadata = {
# {metadata.title}
In this chapter, you'll learn how to create and execute custom scripts from Medusa's CLI tool.
In this chapter, you'll learn how to create and execute custom scripts using Medusa's CLI tool.
## What is a Custom CLI Script?
A custom CLI script is a function to execute through Medusa's CLI tool. This is useful when creating custom Medusa tooling to run through the CLI.
A custom CLI script is a function that you can execute using Medusa's CLI tool. It is useful when you need a script that has access to the [Medusa container](../medusa-container/page.mdx) and can be executed using Medusa's CLI.
For example, you can create a custom CLI script that:
- [Seeds data into the database](./seed-data/page.mdx).
- Runs a script before starting the Medusa application.
---
## How to Create a Custom CLI Script?
To create a custom CLI script, create a TypeScript or JavaScript file under the `src/scripts` directory. The file must default export a function.
To create a custom CLI script, create a TypeScript or JavaScript file under the `src/scripts` directory. The file must export a function by default.
For example, create the file `src/scripts/my-script.ts` with the following content:
@@ -37,13 +42,13 @@ export default async function myScript({ container }: ExecArgs) {
}
```
The function receives as a parameter an object having a `container` property, which is an instance of the Medusa Container. Use it to resolve resources in your Medusa application.
The function receives as a parameter an object with a `container` property, which is an instance of the Medusa Container. Use it to resolve resources in your Medusa application.
---
## How to Run Custom CLI Script?
## How to Run a Custom CLI Script?
To run the custom CLI script, run the Medusa CLI's `exec` command:
To run a custom CLI script, run the Medusa CLI's `exec` command:
```bash
npx medusa exec ./src/scripts/my-script.ts
@@ -55,7 +60,7 @@ npx medusa exec ./src/scripts/my-script.ts
Your script can accept arguments from the command line. Arguments are passed to the function's object parameter in the `args` property.
For example:
For example, create the following CLI script that logs the command line arguments:
```ts
import { ExecArgs } from "@medusajs/framework/types"
@@ -65,8 +70,32 @@ export default async function myScript({ args }: ExecArgs) {
}
```
Then, pass the arguments in the `exec` command after the file path:
Then, run the script with the `exec` command and pass arguments after the script's path.
```bash
npx medusa exec ./src/scripts/my-script.ts arg1 arg2
```
---
## Run Custom Script on Application Startup
In some cases, you may need to perform an action when the Medusa application starts.
If the action is related to a module, you should use a [loader](../modules/loaders/page.mdx). Otherwise, you can create a custom CLI script and run it before starting the Medusa application.
To run a custom script on application startup, modify the `dev` and `start` commands in `package.json` to execute your script first.
For example:
```json title="package.json"
{
"scripts": {
"startup": "medusa exec ./src/scripts/startup.ts",
"dev": "npm run startup && medusa develop",
"start": "npm run startup && medusa start"
}
}
```
The `startup` script will run every time you run the Medusa application.

View File

@@ -4,26 +4,26 @@ export const metadata = {
# {metadata.title}
In this document, you'll learn about the expected files and directories in your module.
In this chapter, you'll learn about the expected files and directories in your module.
![Module Directory Structure Example](https://res.cloudinary.com/dza7lstvk/image/upload/v1714379976/Medusa%20Book/modules-dir-overview_nqq7ne.jpg)
## index.ts
The `index.ts` file in the root of your module's directory is the only required file. It must export the module's definition as explained in a [previous chapter](../page.mdx).
The `index.ts` file is in the root of your module's directory and exports the module's definition as explained in the [Modules](../page.mdx) chapter.
---
## service.ts
A module must have a main service. It's created in the `service.ts` file at the root of your module directory as explained in a [previous chapter](../page.mdx).
A module must have a main service that contains the module's business logic. It's created in the `service.ts` file at the root of your module directory as explained in the [Modules](../page.mdx) chapter.
---
## Other Directories
The following directories are optional and their content are explained more in the following chapters:
The following directories are optional and you can choose to create them based on your module's functionality:
- `models`: Holds the data models representing tables in the database.
- `migrations`: Holds the migration files used to reflect changes on the database.
- `loaders`: Holds the scripts to run on the Medusa application's start-up.
- `models`: Holds the [data models](../../data-models/page.mdx) representing tables in the database.
- `migrations`: Holds the [migration](../../data-models/write-migration/page.mdx) files used to reflect changes on the database.
- `loaders`: Holds the [script files to run on the application's startup](../loaders/page.mdx) when Medusa loads the module.

View File

@@ -1,14 +1,20 @@
export const metadata = {
title: `${pageNumber} Scheduled Jobs Number of Executions`,
title: `${pageNumber} Scheduled Job Number of Executions`,
}
# {metadata.title}
In this chapter, you'll learn how to set a limit on the number of times a scheduled job is executed.
## numberOfExecutions Option
## Default Number of Scheduled Job Executions
The export configuration object of the scheduled job accepts an optional property `numberOfExecutions`. Its value is a number indicating how many times the scheduled job can be executed during the Medusa application's runtime.
By default, a scheduled job is executed whenever it matches its specified pattern. For example, if you set a scheduled job to run every five minutes, it will run every five minutes until you stop the Medusa application.
---
## Configure Number of Scheduled Job Executions
To execute a scheduled job a specific number of times only, you can configure it with the `numberOfExecutions` option. Its value is the number of times the scheduled job can be executed during the Medusa application's runtime.
For example:
@@ -18,7 +24,7 @@ export const highlights = [
```ts highlights={highlights}
export default async function myCustomJob() {
console.log("I'll be executed three times only.")
console.log("I'll be executed only three times.")
}
export const config = {
@@ -31,10 +37,10 @@ export const config = {
The above scheduled job has the `numberOfExecutions` configuration set to `3`.
So, it'll only execute 3 times, each every minute, then it won't be executed anymore.
So, Medusa will execute this job only 3 times, once every minute, and then it won't be executed anymore during the current runtime.
<Note>
### Configuration is Per Application Runtime
If you restart the Medusa application, the scheduled job will be executed again until reaching the number of executions specified.
Medusa tracks the number of executions for a scheduled job during its current runtime. Once the application stops, the next time you start it, the counter will be reset to `0`.
</Note>
So, if you restart the Medusa application, the scheduled job will be executed again until it reaches the number of executions specified.

View File

@@ -10,14 +10,14 @@ In this chapter, youll learn about scheduled jobs and how to use them.
When building your commerce application, you may need to automate tasks and run them repeatedly at a specific schedule. For example, you need to automatically sync products to a third-party service once a day.
In other commerce platforms, this feature isn't natively supported. Instead, you have to setup a separate application to execute cron jobs, which adds complexity as to how you expose this task to be executed in a cron job, or how do you debug it when it's not running within the platform's tooling.
In other commerce platforms, this feature isn't natively supported. Instead, you have to setup a separate application to execute cron jobs, which adds complexity as to how you expose this task to be executed in a cron job, or how you debug it when it's not running within the platform's tooling.
Medusa removes this overhead by supporting this feature natively with scheduled jobs. A scheduled job is an asynchronous function that the Medusa application runs at the interval you specify during the Medusa application's runtime. Your efforts are only spent on implementing the functionality performed by the job, such as syncing products to an ERP.
<Note title="Don't use scheduled jobs if" type="error">
- You want the action to execute at a specified schedule while the Medusa application **isn't** running. Instead, use the operating system's equivalent of a cron job.
- You want to execute the action once when the application loads. Use [loaders](../modules/loaders/page.mdx) instead.
- You want the action to execute at a specified schedule while the Medusa application **isn't** running. Instead, create a [custom CLI script](../custom-cli-scripts/page.mdx) and execute it using the operating system's equivalent of a cron job.
- You want to execute the action once when the application loads. Use [loaders](../modules/loaders/page.mdx) or [custom CLI scripts](../custom-cli-scripts/page.mdx#run-custom-script-on-application-startup) instead.
- You want to execute the action if an event occurs. Use [subscribers](../events-and-subscribers/page.mdx) instead.
</Note>
@@ -63,7 +63,7 @@ You also export a `config` object that has the following properties:
- `name`: A unique name for the job.
- `schedule`: A string that holds a [cron expression](https://crontab.guru/) indicating the schedule to run the job.
This scheduled job executes every minute and logs into the terminal `Greeting!`.
This scheduled job executes every minute and logs into the terminal the message `Greeting!`.
### Test the Scheduled Job
@@ -83,11 +83,11 @@ info: Greeting!
## Example: Sync Products Once a Day
In this section, you'll find a brief example of how you use a scheduled job to sync products to a third-party service.
In a realistic scenario like syncing products to an ERP once a day, you should create a [workflow](../workflows/page.mdx) and execute it in a scheduled job.
When implementing flows spanning across systems or [modules](../modules/page.mdx), you use [workflows](../workflows/page.mdx). A workflow is a task made up of a series of steps, and you construct it like you would a regular function, but it's a special function that supports rollback mechanism in case of errors, background execution, and more.
A workflow is a task made up of a series of steps, and you construct it like you would a regular function, but it's a special function that supports rollback mechanism in case of errors, background execution, and more.
You can learn how to create a workflow in [this chapter](../workflows/page.mdx), but this example assumes you already have a `syncProductToErpWorkflow` implemented. To execute this workflow once a day, create a scheduled job at `src/jobs/sync-products.ts` with the following content:
You can learn how to create a workflow in the [Workflows](../workflows/page.mdx) chapter, but this example assumes you already have a `syncProductToErpWorkflow` implemented. To execute this workflow once a day, create a scheduled job at `src/jobs/sync-products.ts` with the following content:
```ts title="src/jobs/sync-products.ts"
import { MedusaContainer } from "@medusajs/framework/types"

View File

@@ -1,5 +1,5 @@
export const generatedEditDates = {
"app/learn/fundamentals/scheduled-jobs/page.mdx": "2024-12-09T10:51:40.570Z",
"app/learn/fundamentals/scheduled-jobs/page.mdx": "2025-07-25T15:56:17.926Z",
"app/learn/fundamentals/workflows/page.mdx": "2024-12-09T14:45:17.837Z",
"app/learn/deployment/page.mdx": "2025-07-18T15:25:33.249Z",
"app/learn/page.mdx": "2025-06-27T11:39:15.941Z",
@@ -13,13 +13,13 @@ export const generatedEditDates = {
"app/learn/fundamentals/workflows/workflow-timeout/page.mdx": "2025-04-24T13:15:14.472Z",
"app/learn/fundamentals/workflows/parallel-steps/page.mdx": "2025-03-24T06:53:36.918Z",
"app/learn/fundamentals/medusa-container/page.mdx": "2025-07-25T13:18:36.859Z",
"app/learn/fundamentals/api-routes/page.mdx": "2024-12-04T11:02:57.134Z",
"app/learn/fundamentals/modules/modules-directory-structure/page.mdx": "2024-12-09T10:32:46.839Z",
"app/learn/fundamentals/api-routes/page.mdx": "2025-07-25T15:19:33.365Z",
"app/learn/fundamentals/modules/modules-directory-structure/page.mdx": "2025-07-25T15:40:20.362Z",
"app/learn/fundamentals/events-and-subscribers/page.mdx": "2025-05-16T13:40:16.111Z",
"app/learn/fundamentals/modules/container/page.mdx": "2025-05-21T15:07:12.059Z",
"app/learn/fundamentals/workflows/execute-another-workflow/page.mdx": "2024-12-09T15:56:22.895Z",
"app/learn/fundamentals/modules/loaders/page.mdx": "2025-06-16T13:34:16.462Z",
"app/learn/fundamentals/admin/widgets/page.mdx": "2025-07-25T12:55:14.744Z",
"app/learn/fundamentals/admin/widgets/page.mdx": "2025-07-25T15:08:07.035Z",
"app/learn/fundamentals/data-models/page.mdx": "2025-03-18T07:55:56.252Z",
"app/learn/fundamentals/modules/remote-link/page.mdx": "2024-09-30T08:43:53.127Z",
"app/learn/fundamentals/api-routes/protected-routes/page.mdx": "2025-06-19T16:04:36.064Z",
@@ -40,16 +40,16 @@ export const generatedEditDates = {
"app/learn/fundamentals/workflows/compensation-function/page.mdx": "2025-04-24T13:16:00.941Z",
"app/learn/fundamentals/modules/service-factory/page.mdx": "2025-03-18T15:14:13.486Z",
"app/learn/fundamentals/modules/module-links/page.mdx": "2024-09-30T08:43:53.126Z",
"app/learn/fundamentals/scheduled-jobs/execution-number/page.mdx": "2024-10-21T13:30:21.371Z",
"app/learn/fundamentals/scheduled-jobs/execution-number/page.mdx": "2025-07-25T15:54:56.135Z",
"app/learn/fundamentals/api-routes/parameters/page.mdx": "2025-02-14T08:34:03.184Z",
"app/learn/fundamentals/api-routes/http-methods/page.mdx": "2024-10-21T13:30:21.367Z",
"app/learn/fundamentals/api-routes/http-methods/page.mdx": "2025-07-25T15:12:29.347Z",
"app/learn/fundamentals/admin/tips/page.mdx": "2025-05-26T14:58:56.390Z",
"app/learn/fundamentals/api-routes/cors/page.mdx": "2025-03-11T08:54:26.281Z",
"app/learn/fundamentals/admin/ui-routes/page.mdx": "2025-07-25T06:58:26.149Z",
"app/learn/fundamentals/api-routes/middlewares/page.mdx": "2025-07-18T15:20:25.735Z",
"app/learn/fundamentals/modules/isolation/page.mdx": "2025-05-21T15:10:15.499Z",
"app/learn/fundamentals/data-models/index/page.mdx": "2025-03-18T07:59:07.798Z",
"app/learn/fundamentals/custom-cli-scripts/page.mdx": "2024-10-23T07:08:55.898Z",
"app/learn/fundamentals/custom-cli-scripts/page.mdx": "2025-07-25T15:32:47.587Z",
"app/learn/debugging-and-testing/testing-tools/integration-tests/api-routes/page.mdx": "2025-03-18T15:06:27.864Z",
"app/learn/debugging-and-testing/testing-tools/integration-tests/page.mdx": "2024-12-09T15:52:01.019Z",
"app/learn/debugging-and-testing/testing-tools/integration-tests/workflows/page.mdx": "2025-02-11T15:56:03.835Z",
@@ -57,7 +57,7 @@ export const generatedEditDates = {
"app/learn/debugging-and-testing/testing-tools/unit-tests/module-example/page.mdx": "2024-09-02T11:04:27.232Z",
"app/learn/debugging-and-testing/testing-tools/unit-tests/page.mdx": "2024-09-02T11:03:26.997Z",
"app/learn/fundamentals/modules/service-constraints/page.mdx": "2025-03-18T15:12:46.006Z",
"app/learn/fundamentals/api-routes/responses/page.mdx": "2024-10-21T13:30:21.367Z",
"app/learn/fundamentals/api-routes/responses/page.mdx": "2025-07-25T15:17:39.986Z",
"app/learn/fundamentals/api-routes/validation/page.mdx": "2025-03-24T06:52:47.896Z",
"app/learn/fundamentals/api-routes/errors/page.mdx": "2025-06-19T16:09:08.563Z",
"app/learn/fundamentals/admin/constraints/page.mdx": "2025-07-21T08:20:43.223Z",

View File

@@ -6064,13 +6064,13 @@ For more customizations related to routes, refer to the [Routing Customizations
# Admin Widgets
In this chapter, youll learn more about widgets and how to use them.
In this chapter, youll learn about widgets and how to use them.
## What is an Admin Widget?
The Medusa Admin dashboard's pages are customizable to insert widgets of custom content in pre-defined injection zones. You create these widgets as React components that allow admin users to perform custom actions.
The Medusa Admin's pages are customizable for inserting widgets of custom content in pre-defined injection zones. For example, you can add a widget on the product details page that allows admin users to sync products to a third-party service.
For example, you can add a widget on the product details page that allow admin users to sync products to a third-party service.
You create these widgets as React components that render the content and functionality of the widget.
***
@@ -6080,7 +6080,10 @@ For example, you can add a widget on the product details page that allow admin u
- [Medusa application installed](https://docs.medusajs.com/learn/installation/index.html.md)
You create a widget in a `.tsx` file under the `src/admin/widgets` directory. The files default export must be the widget, which is the React component that renders the custom content. The file must also export the widgets configurations indicating where to insert the widget.
You create a widget in a `.tsx` file under the `src/admin/widgets` directory. The file must export:
1. A React component that renders the widget. This will be the file's default export.
2. The widgets configurations indicating where to insert the widget.
For example, create the file `src/admin/widgets/product-widget.tsx` with the following content:
@@ -6109,12 +6112,12 @@ export const config = defineWidgetConfig({
export default ProductWidget
```
You export the `ProductWidget` component, which shows the heading `Product Widget`. In the widget, you use [Medusa UI](https://docs.medusajs.com/ui/index.html.md), a package that Medusa maintains to allow you to customize the dashboard with the same components used to build it.
To export the widget's configurations, you use `defineWidgetConfig` from the Admin Extension SDK. It accepts as a parameter an object with the `zone` property, whose value is a string or an array of strings, each being the name of the zone to inject the widget into.
In the example above, the widget is injected at the top of a products details.
You export the `ProductWidget` component, which displays the heading `Product Widget`. In the widget, you use [Medusa UI](https://docs.medusajs.com/ui/index.html.md) to customize the dashboard with the same components used to build it.
To export the widget's configuration, you use `defineWidgetConfig` from the Admin Extension SDK. It accepts an object as a parameter with the `zone` property, whose value is a string or an array of strings, each being the name of the zone to inject the widget into.
The widget component must be created as an arrow function.
### Test the Widget
@@ -6129,9 +6132,9 @@ Then, open a products details page. Youll find your custom widget at the t
***
## Props Passed in Detail Pages
## Props Passed to Widgets on Detail Pages
Widgets that are injected into a details page receive a `data` prop, which is the main data of the details page.
Widgets that are injected into a detail page receive a `data` prop, which is the main data of the details page.
For example, a widget injected into the `product.details.before` zone receives the product's details in the `data` prop:
@@ -6170,15 +6173,17 @@ The props type is `DetailWidgetProps`, and it accepts as a type argument the exp
***
## Injection Zone
## Injection Zones List
Refer to [this reference](https://docs.medusajs.com/resources/admin-widget-injection-zones/index.html.md) for the full list of injection zones and their props.
Refer to the [Admin Widget Injection Zones](https://docs.medusajs.com/resources/admin-widget-injection-zones/index.html.md) reference for the full list of injection zones and their props.
***
## Admin Components List
To build admin customizations that match the Medusa Admin's designs and layouts, refer to [this guide](https://docs.medusajs.com/resources/admin-components/index.html.md) to find common components.
While the Medusa Admin uses the [Medusa UI](https://docs.medusajs.com/ui/index.html.md) components, it also expands on them for styling and design purposes.
To build admin customizations that match the Medusa Admin's designs and layouts, refer to the [Admin Components](https://docs.medusajs.com/resources/admin-components/index.html.md) guide. You'll find components like `Header`, `JSON View`, and more that match the Medusa Admin's design.
***
@@ -6186,7 +6191,7 @@ To build admin customizations that match the Medusa Admin's designs and layouts,
In some cases, you may want to show a widget only if certain conditions are met. For example, you may want to show a widget only if the product has a brand.
To disable the widget from showing, return an empty fragment from the widget component:
To prevent the widget from showing, return an empty fragment from the widget component:
```tsx title="src/admin/widgets/product-widget.tsx"
import { defineWidgetConfig } from "@medusajs/admin-sdk"
@@ -6690,7 +6695,7 @@ In this chapter, you'll learn about how to add new API routes for each HTTP meth
## HTTP Method Handler
An API route is created for every HTTP method you export a handler function for in a route file.
An API route is created for every HTTP method you export a handler function for in a `route.ts` or `route.js` file.
Allowed HTTP methods are: `GET`, `POST`, `DELETE`, `PUT`, `PATCH`, `OPTIONS`, and `HEAD`.
@@ -6721,10 +6726,10 @@ export const POST = async (
}
```
This adds two API Routes:
This file adds two API Routes:
- A `GET` route at `http://localhost:9000/hello-world`.
- A `POST` route at `http://localhost:9000/hello-world`.
- A `GET` API route at `http://localhost:9000/hello-world`.
- A `POST` API route at `http://localhost:9000/hello-world`.
# Middlewares
@@ -7160,25 +7165,25 @@ Some examples of when you might want to replicate an API route include:
# API Routes
In this chapter, youll learn what API Routes are and how to create them.
In this chapter, youll learn what API routes are and how to create them.
## What is an API Route?
An API Route is an endpoint. It exposes commerce features to external applications, such as storefronts, the admin dashboard, or third-party systems.
An API route is a REST endpoint that exposes commerce features to external applications, such as storefronts, the admin dashboard, or third-party systems.
The Medusa core application provides a set of admin and store API routes out-of-the-box. You can also create custom API routes to expose your custom functionalities.
The Medusa core application provides a set of [admin](https://docs.medusajs.com/api/admin) and [store](https://docs.medusajs.com/api/store) API routes out-of-the-box. You can also create custom API routes to expose your custom functionalities.
***
## How to Create an API Route?
An API Route is created in a TypeScript or JavaScript file under the `src/api` directory of your Medusa application. The files name must be `route.ts` or `route.js`.
You can create an API route in a TypeScript or JavaScript file under the `src/api` directory of your Medusa application. The files name must be `route.ts` or `route.js`.
![Example of API route in the application's directory structure](https://res.cloudinary.com/dza7lstvk/image/upload/v1732808645/Medusa%20Book/route-dir-overview_dqgzmk.jpg)
Each file exports API Route handler functions for at least one HTTP method (`GET`, `POST`, `DELETE`, etc…).
Each file exports API route handler functions for at least one HTTP method (`GET`, `POST`, `DELETE`, etc…).
For example, to create a `GET` API Route at `/hello-world`, create the file `src/api/hello-world/route.ts` with the following content:
For example, to create a `GET` API route at `/hello-world`, create the file `src/api/hello-world/route.ts` with the following content:
```ts title="src/api/hello-world/route.ts"
import type {
@@ -7196,7 +7201,7 @@ export const GET = (
}
```
### Test API Route
### Test the API Route
To test the API route above, start the Medusa application:
@@ -7204,17 +7209,25 @@ To test the API route above, start the Medusa application:
npm run dev
```
Then, send a `GET` request to the `/hello-world` API Route:
Then, send a `GET` request to the `/hello-world` API route:
```bash
curl http://localhost:9000/hello-world
```
You should receive the following response:
```json
{
"message": "[GET] Hello world!"
}
```
***
## When to Use API Routes
## Next Chapters: Learn More About API Routes
You're exposing custom functionality to be used by a storefront, admin dashboard, or any external application.
Follow the next chapters to learn about the different HTTP methods you can use in API routes, parameters and validation, protecting routes, and more.
# API Route Parameters
@@ -7804,7 +7817,7 @@ In this chapter, you'll learn how to send a response in your API route.
## Send a JSON Response
To send a JSON response, use the `json` method of the `MedusaResponse` object passed as the second parameter of your API route handler.
To send a JSON response, use the `json` method of the `MedusaResponse` object that is passed as the second parameter of your API route handler.
For example:
@@ -7888,10 +7901,10 @@ export const GET = async (
The `writeHead` method accepts two parameters:
1. The first one is the response's status code.
2. The second is an object of key-value pairs to set the headers of the response.
1. The first parameter is the response's status code.
2. The second parameter is an object of key-value pairs to set the response headers.
This API route opens a stream by setting the `Content-Type` in the header to `text/event-stream`. It then simulates a stream by creating an interval that writes the stream data every three seconds.
This API route opens a stream by setting the `Content-Type` to `text/event-stream`. It then simulates a stream by creating an interval that writes the stream data every three seconds.
***
@@ -8234,17 +8247,22 @@ To see different examples and learn more about creating a validation schema, ref
# Custom CLI Scripts
In this chapter, you'll learn how to create and execute custom scripts from Medusa's CLI tool.
In this chapter, you'll learn how to create and execute custom scripts using Medusa's CLI tool.
## What is a Custom CLI Script?
A custom CLI script is a function to execute through Medusa's CLI tool. This is useful when creating custom Medusa tooling to run through the CLI.
A custom CLI script is a function that you can execute using Medusa's CLI tool. It is useful when you need a script that has access to the [Medusa container](https://docs.medusajs.com/learn/fundamentals/medusa-container/index.html.md) and can be executed using Medusa's CLI.
For example, you can create a custom CLI script that:
- [Seeds data into the database](https://docs.medusajs.com/learn/fundamentals/custom-cli-scripts/seed-data/index.html.md).
- Runs a script before starting the Medusa application.
***
## How to Create a Custom CLI Script?
To create a custom CLI script, create a TypeScript or JavaScript file under the `src/scripts` directory. The file must default export a function.
To create a custom CLI script, create a TypeScript or JavaScript file under the `src/scripts` directory. The file must export a function by default.
For example, create the file `src/scripts/my-script.ts` with the following content:
@@ -8267,13 +8285,13 @@ export default async function myScript({ container }: ExecArgs) {
}
```
The function receives as a parameter an object having a `container` property, which is an instance of the Medusa Container. Use it to resolve resources in your Medusa application.
The function receives as a parameter an object with a `container` property, which is an instance of the Medusa Container. Use it to resolve resources in your Medusa application.
***
## How to Run Custom CLI Script?
## How to Run a Custom CLI Script?
To run the custom CLI script, run the Medusa CLI's `exec` command:
To run a custom CLI script, run the Medusa CLI's `exec` command:
```bash
npx medusa exec ./src/scripts/my-script.ts
@@ -8285,7 +8303,7 @@ npx medusa exec ./src/scripts/my-script.ts
Your script can accept arguments from the command line. Arguments are passed to the function's object parameter in the `args` property.
For example:
For example, create the following CLI script that logs the command line arguments:
```ts
import { ExecArgs } from "@medusajs/framework/types"
@@ -8295,12 +8313,36 @@ export default async function myScript({ args }: ExecArgs) {
}
```
Then, pass the arguments in the `exec` command after the file path:
Then, run the script with the `exec` command and pass arguments after the script's path.
```bash
npx medusa exec ./src/scripts/my-script.ts arg1 arg2
```
***
## Run Custom Script on Application Startup
In some cases, you may need to perform an action when the Medusa application starts.
If the action is related to a module, you should use a [loader](https://docs.medusajs.com/learn/fundamentals/modules/loaders/index.html.md). Otherwise, you can create a custom CLI script and run it before starting the Medusa application.
To run a custom script on application startup, modify the `dev` and `start` commands in `package.json` to execute your script first.
For example:
```json title="package.json"
{
"scripts": {
"startup": "medusa exec ./src/scripts/startup.ts",
"dev": "npm run startup && medusa develop",
"start": "npm run startup && medusa start"
}
}
```
The `startup` script will run every time you run the Medusa application.
# Seed Data with Custom CLI Script
@@ -11140,13 +11182,13 @@ In this chapter, you'll learn about the types Medusa automatically generates und
Medusa automatically generates TypeScript types for:
![Diagram showcasing the directory structure of the .medusa directory, highlighting the types folder and its contents.](https://res.cloudinary.com/dza7lstvk/image/upload/v1753448927/Medusa%20Book/generated-types-dir_bmvdts.jpg)
1. Data models collected in the [Query's graph](https://docs.medusajs.com/learn/fundamentals/module-links/query#querying-the-graph/index.html.md). These types provide you with auto-completion and type checking when using Query.
- Generated data model types are located in the `.medusa/types/query-entry-points.d.ts` file.
2. Modules registered in the [Medusa container](https://docs.medusajs.com/learn/fundamentals/medusa-container/index.html.md). These types provide you with auto-completion and type checking when resolving modules from the container.
- Generated module registration names are located in the `.medusa/types/modules-bindings.d.ts` file.
![Diagram showcasing the directory structure of the .medusa directory, highlighting the types folder and its contents.](https://res.cloudinary.com/dza7lstvk/image/upload/v1753448927/Medusa%20Book/generated-types-dir_bmvdts.jpg)
***
## How to Trigger Type Generation?
@@ -15105,29 +15147,29 @@ You can now resolve the MongoDB Module's main service in your customizations to
# Modules Directory Structure
In this document, you'll learn about the expected files and directories in your module.
In this chapter, you'll learn about the expected files and directories in your module.
![Module Directory Structure Example](https://res.cloudinary.com/dza7lstvk/image/upload/v1714379976/Medusa%20Book/modules-dir-overview_nqq7ne.jpg)
## index.ts
The `index.ts` file in the root of your module's directory is the only required file. It must export the module's definition as explained in a [previous chapter](https://docs.medusajs.com/learn/fundamentals/modules/index.html.md).
The `index.ts` file is in the root of your module's directory and exports the module's definition as explained in the [Modules](https://docs.medusajs.com/learn/fundamentals/modules/index.html.md) chapter.
***
## service.ts
A module must have a main service. It's created in the `service.ts` file at the root of your module directory as explained in a [previous chapter](https://docs.medusajs.com/learn/fundamentals/modules/index.html.md).
A module must have a main service that contains the module's business logic. It's created in the `service.ts` file at the root of your module directory as explained in the [Modules](https://docs.medusajs.com/learn/fundamentals/modules/index.html.md) chapter.
***
## Other Directories
The following directories are optional and their content are explained more in the following chapters:
The following directories are optional and you can choose to create them based on your module's functionality:
- `models`: Holds the data models representing tables in the database.
- `migrations`: Holds the migration files used to reflect changes on the database.
- `loaders`: Holds the scripts to run on the Medusa application's start-up.
- `models`: Holds the [data models](https://docs.medusajs.com/learn/fundamentals/data-models/index.html.md) representing tables in the database.
- `migrations`: Holds the [migration](https://docs.medusajs.com/learn/fundamentals/data-models/write-migration/index.html.md) files used to reflect changes on the database.
- `loaders`: Holds the [script files to run on the application's startup](https://docs.medusajs.com/learn/fundamentals/modules/loaders/index.html.md) when Medusa loads the module.
# Multiple Services in a Module
@@ -16408,19 +16450,25 @@ For example, in a plugin, you can define a module that integrates a third-party
The next chapter explains how you can create and publish a plugin.
# Scheduled Jobs Number of Executions
# Scheduled Job Number of Executions
In this chapter, you'll learn how to set a limit on the number of times a scheduled job is executed.
## numberOfExecutions Option
## Default Number of Scheduled Job Executions
The export configuration object of the scheduled job accepts an optional property `numberOfExecutions`. Its value is a number indicating how many times the scheduled job can be executed during the Medusa application's runtime.
By default, a scheduled job is executed whenever it matches its specified pattern. For example, if you set a scheduled job to run every five minutes, it will run every five minutes until you stop the Medusa application.
***
## Configure Number of Scheduled Job Executions
To execute a scheduled job a specific number of times only, you can configure it with the `numberOfExecutions` option. Its value is the number of times the scheduled job can be executed during the Medusa application's runtime.
For example:
```ts highlights={highlights}
export default async function myCustomJob() {
console.log("I'll be executed three times only.")
console.log("I'll be executed only three times.")
}
export const config = {
@@ -16433,9 +16481,13 @@ export const config = {
The above scheduled job has the `numberOfExecutions` configuration set to `3`.
So, it'll only execute 3 times, each every minute, then it won't be executed anymore.
So, Medusa will execute this job only 3 times, once every minute, and then it won't be executed anymore during the current runtime.
If you restart the Medusa application, the scheduled job will be executed again until reaching the number of executions specified.
### Configuration is Per Application Runtime
Medusa tracks the number of executions for a scheduled job during its current runtime. Once the application stops, the next time you start it, the counter will be reset to `0`.
So, if you restart the Medusa application, the scheduled job will be executed again until it reaches the number of executions specified.
# Scheduled Jobs
@@ -16446,12 +16498,12 @@ In this chapter, youll learn about scheduled jobs and how to use them.
When building your commerce application, you may need to automate tasks and run them repeatedly at a specific schedule. For example, you need to automatically sync products to a third-party service once a day.
In other commerce platforms, this feature isn't natively supported. Instead, you have to setup a separate application to execute cron jobs, which adds complexity as to how you expose this task to be executed in a cron job, or how do you debug it when it's not running within the platform's tooling.
In other commerce platforms, this feature isn't natively supported. Instead, you have to setup a separate application to execute cron jobs, which adds complexity as to how you expose this task to be executed in a cron job, or how you debug it when it's not running within the platform's tooling.
Medusa removes this overhead by supporting this feature natively with scheduled jobs. A scheduled job is an asynchronous function that the Medusa application runs at the interval you specify during the Medusa application's runtime. Your efforts are only spent on implementing the functionality performed by the job, such as syncing products to an ERP.
- You want the action to execute at a specified schedule while the Medusa application **isn't** running. Instead, use the operating system's equivalent of a cron job.
- You want to execute the action once when the application loads. Use [loaders](https://docs.medusajs.com/learn/fundamentals/modules/loaders/index.html.md) instead.
- You want the action to execute at a specified schedule while the Medusa application **isn't** running. Instead, create a [custom CLI script](https://docs.medusajs.com/learn/fundamentals/custom-cli-scripts/index.html.md) and execute it using the operating system's equivalent of a cron job.
- You want to execute the action once when the application loads. Use [loaders](https://docs.medusajs.com/learn/fundamentals/modules/loaders/index.html.md) or [custom CLI scripts](https://docs.medusajs.com/learn/fundamentals/custom-cli-scripts#run-custom-script-on-application-startup/index.html.md) instead.
- You want to execute the action if an event occurs. Use [subscribers](https://docs.medusajs.com/learn/fundamentals/events-and-subscribers/index.html.md) instead.
***
@@ -16486,7 +16538,7 @@ You also export a `config` object that has the following properties:
- `name`: A unique name for the job.
- `schedule`: A string that holds a [cron expression](https://crontab.guru/) indicating the schedule to run the job.
This scheduled job executes every minute and logs into the terminal `Greeting!`.
This scheduled job executes every minute and logs into the terminal the message `Greeting!`.
### Test the Scheduled Job
@@ -16506,11 +16558,11 @@ info: Greeting!
## Example: Sync Products Once a Day
In this section, you'll find a brief example of how you use a scheduled job to sync products to a third-party service.
In a realistic scenario like syncing products to an ERP once a day, you should create a [workflow](https://docs.medusajs.com/learn/fundamentals/workflows/index.html.md) and execute it in a scheduled job.
When implementing flows spanning across systems or [modules](https://docs.medusajs.com/learn/fundamentals/modules/index.html.md), you use [workflows](https://docs.medusajs.com/learn/fundamentals/workflows/index.html.md). A workflow is a task made up of a series of steps, and you construct it like you would a regular function, but it's a special function that supports rollback mechanism in case of errors, background execution, and more.
A workflow is a task made up of a series of steps, and you construct it like you would a regular function, but it's a special function that supports rollback mechanism in case of errors, background execution, and more.
You can learn how to create a workflow in [this chapter](https://docs.medusajs.com/learn/fundamentals/workflows/index.html.md), but this example assumes you already have a `syncProductToErpWorkflow` implemented. To execute this workflow once a day, create a scheduled job at `src/jobs/sync-products.ts` with the following content:
You can learn how to create a workflow in the [Workflows](https://docs.medusajs.com/learn/fundamentals/workflows/index.html.md) chapter, but this example assumes you already have a `syncProductToErpWorkflow` implemented. To execute this workflow once a day, create a scheduled job at `src/jobs/sync-products.ts` with the following content:
```ts title="src/jobs/sync-products.ts"
import { MedusaContainer } from "@medusajs/framework/types"