diff --git a/www/apps/book/app/learn/fundamentals/admin/widgets/page.mdx b/www/apps/book/app/learn/fundamentals/admin/widgets/page.mdx index 7e5c9dbef2..8a89236b2c 100644 --- a/www/apps/book/app/learn/fundamentals/admin/widgets/page.mdx +++ b/www/apps/book/app/learn/fundamentals/admin/widgets/page.mdx @@ -6,13 +6,13 @@ export const metadata = { # {metadata.title} -In this chapter, you’ll learn more about widgets and how to use them. +In this chapter, you’ll 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 file’s default export must be the widget, which is the React component that renders the custom content. The file must also export the widget’s 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 widget’s 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 product’s 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. + The widget component must be created as an arrow function. @@ -83,9 +86,9 @@ Then, open a product’s details page. You’ll 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" diff --git a/www/apps/book/app/learn/fundamentals/api-routes/http-methods/page.mdx b/www/apps/book/app/learn/fundamentals/api-routes/http-methods/page.mdx index d0f5a9d7f1..92ac95cf8a 100644 --- a/www/apps/book/app/learn/fundamentals/api-routes/http-methods/page.mdx +++ b/www/apps/book/app/learn/fundamentals/api-routes/http-methods/page.mdx @@ -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`. \ No newline at end of file +- A `GET` API route at `http://localhost:9000/hello-world`. +- A `POST` API route at `http://localhost:9000/hello-world`. \ No newline at end of file diff --git a/www/apps/book/app/learn/fundamentals/api-routes/page.mdx b/www/apps/book/app/learn/fundamentals/api-routes/page.mdx index cb75d18035..f8916cc444 100644 --- a/www/apps/book/app/learn/fundamentals/api-routes/page.mdx +++ b/www/apps/book/app/learn/fundamentals/api-routes/page.mdx @@ -4,25 +4,25 @@ export const metadata = { # {metadata.title} -In this chapter, you’ll learn what API Routes are and how to create them. +In this chapter, you’ll 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 file’s 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 file’s 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 - - -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. \ No newline at end of file diff --git a/www/apps/book/app/learn/fundamentals/api-routes/responses/page.mdx b/www/apps/book/app/learn/fundamentals/api-routes/responses/page.mdx index bdbbe7a127..559930830e 100644 --- a/www/apps/book/app/learn/fundamentals/api-routes/responses/page.mdx +++ b/www/apps/book/app/learn/fundamentals/api-routes/responses/page.mdx @@ -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. --- diff --git a/www/apps/book/app/learn/fundamentals/custom-cli-scripts/page.mdx b/www/apps/book/app/learn/fundamentals/custom-cli-scripts/page.mdx index ed3973ba79..0f1f5775d7 100644 --- a/www/apps/book/app/learn/fundamentals/custom-cli-scripts/page.mdx +++ b/www/apps/book/app/learn/fundamentals/custom-cli-scripts/page.mdx @@ -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. \ No newline at end of file diff --git a/www/apps/book/app/learn/fundamentals/modules/modules-directory-structure/page.mdx b/www/apps/book/app/learn/fundamentals/modules/modules-directory-structure/page.mdx index c49a998a5b..3010edf026 100644 --- a/www/apps/book/app/learn/fundamentals/modules/modules-directory-structure/page.mdx +++ b/www/apps/book/app/learn/fundamentals/modules/modules-directory-structure/page.mdx @@ -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. \ No newline at end of file +- `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. \ No newline at end of file diff --git a/www/apps/book/app/learn/fundamentals/scheduled-jobs/execution-number/page.mdx b/www/apps/book/app/learn/fundamentals/scheduled-jobs/execution-number/page.mdx index dbcca6f573..6a0d6a3605 100644 --- a/www/apps/book/app/learn/fundamentals/scheduled-jobs/execution-number/page.mdx +++ b/www/apps/book/app/learn/fundamentals/scheduled-jobs/execution-number/page.mdx @@ -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. - +### 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`. - +So, if you restart the Medusa application, the scheduled job will be executed again until it reaches the number of executions specified. diff --git a/www/apps/book/app/learn/fundamentals/scheduled-jobs/page.mdx b/www/apps/book/app/learn/fundamentals/scheduled-jobs/page.mdx index 71e80857d0..10cd18522b 100644 --- a/www/apps/book/app/learn/fundamentals/scheduled-jobs/page.mdx +++ b/www/apps/book/app/learn/fundamentals/scheduled-jobs/page.mdx @@ -10,14 +10,14 @@ In this chapter, you’ll 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](../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. @@ -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" diff --git a/www/apps/book/generated/edit-dates.mjs b/www/apps/book/generated/edit-dates.mjs index aa1ec31baa..f57fd30382 100644 --- a/www/apps/book/generated/edit-dates.mjs +++ b/www/apps/book/generated/edit-dates.mjs @@ -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", diff --git a/www/apps/book/public/llms-full.txt b/www/apps/book/public/llms-full.txt index 557900b791..ef75ce2727 100644 --- a/www/apps/book/public/llms-full.txt +++ b/www/apps/book/public/llms-full.txt @@ -6064,13 +6064,13 @@ For more customizations related to routes, refer to the [Routing Customizations # Admin Widgets -In this chapter, you’ll learn more about widgets and how to use them. +In this chapter, you’ll 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 file’s default export must be the widget, which is the React component that renders the custom content. The file must also export the widget’s 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 widget’s 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 product’s 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 product’s details page. You’ll 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, you’ll learn what API Routes are and how to create them. +In this chapter, you’ll 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 file’s 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 file’s 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, you’ll 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"