From a74c900ab15a64e717b1b7a73d74eda1f3c15e4d Mon Sep 17 00:00:00 2001 From: Shahed Nasser Date: Mon, 15 Jul 2024 18:46:10 +0300 Subject: [PATCH] docs: add documentation for migration generate cli tool (#8128) * docs: add documentation for migration generate cli tool * changed migrations details in marketplace recipe * added generated oas files to action * vale + lint fixes * don't import from src in medusa-config.js * fix generate command in recipe * fix module name --- .../workflows/generate-preview-references.yml | 4 +- .../api-routes/protected-routes/page.mdx | 15 +- .../data-models/manage-relationships/page.mdx | 8 +- .../data-models/write-migration/page.mdx | 58 +++++ .../modules/module-links/page.mdx | 2 +- .../modules/remote-query/page.mdx | 4 +- .../workflows/advanced-example/page.mdx | 16 +- .../workflows/conditions/page.mdx | 2 +- .../constructor-constraints/page.mdx | 4 +- .../book/app/basics/commerce-modules/page.mdx | 10 +- www/apps/book/app/basics/data-models/page.mdx | 60 +---- .../basics/events-and-subscribers/page.mdx | 6 +- .../book/app/basics/medusa-container/page.mdx | 9 +- .../app/basics/modules-and-services/page.mdx | 9 +- www/apps/book/sidebar.mjs | 4 + .../app/commerce-modules/order/page.mdx | 2 +- .../order/promotion-adjustments/page.mdx | 2 +- www/apps/resources/app/medusa-cli/page.mdx | 60 +++-- .../marketplace/examples/vendors/page.mdx | 224 ++++++++---------- 19 files changed, 270 insertions(+), 229 deletions(-) create mode 100644 www/apps/book/app/advanced-development/data-models/write-migration/page.mdx diff --git a/.github/workflows/generate-preview-references.yml b/.github/workflows/generate-preview-references.yml index 538241de30..b25fc991b6 100644 --- a/.github/workflows/generate-preview-references.yml +++ b/.github/workflows/generate-preview-references.yml @@ -60,6 +60,8 @@ jobs: base: "develop" title: "chore(docs): Updated API Reference (v2)" labels: "type: chore" - add-paths: www/apps/api-reference/specs + add-paths: | + www/apps/api-reference/specs + www/utils/generated/oas-output branch: "docs/generate-api-ref" branch-suffix: "timestamp" \ No newline at end of file diff --git a/www/apps/book/app/advanced-development/api-routes/protected-routes/page.mdx b/www/apps/book/app/advanced-development/api-routes/protected-routes/page.mdx index afb6362670..1bda9630d8 100644 --- a/www/apps/book/app/advanced-development/api-routes/protected-routes/page.mdx +++ b/www/apps/book/app/advanced-development/api-routes/protected-routes/page.mdx @@ -33,10 +33,13 @@ To disable the authentication guard on custom routes under the `/admin` or `/sto For example: -```ts title="src/api/store/customers/me/custom/route.ts" highlights={[["15"]]} apiTesting testApiUrl="http://localhost:9000/store/customers/me/custom" testApiMethod="GET" +```ts title="src/api/store/customers/me/custom/route.ts" highlights={[["12"]]} apiTesting testApiUrl="http://localhost:9000/store/customers/me/custom" testApiMethod="GET" import type { MedusaRequest, MedusaResponse } from "@medusajs/medusa" -export const GET = async (req: MedusaRequest, res: MedusaResponse) => { +export const GET = async ( + req: MedusaRequest, + res: MedusaResponse +) => { res.json({ message: "Hello", }) @@ -55,7 +58,7 @@ You can access the logged-in customer’s ID in all API routes starting with `/s For example: -```ts title="src/api/store/customers/me/custom/route.ts" highlights={[["16", "", "Access the logged-in customer's ID."]]} collapsibleLines="1-7" expandButtonLabel="Show Imports" +```ts title="src/api/store/customers/me/custom/route.ts" highlights={[["17", "req.auth_context.actor_id", "Access the logged-in customer's ID."]]} collapsibleLines="1-7" expandButtonLabel="Show Imports" import type { AuthenticatedMedusaRequest, MedusaResponse, @@ -89,7 +92,7 @@ You can access the logged-in admin user’s ID in all API Routes starting with ` For example: -```ts title="src/api/admin/custom/route.ts" highlights={[["16", "req.user.userId", "Access the logged-in admin user's ID."]]} collapsibleLines="1-7" expandButtonLabel="Show Imports" +```ts title="src/api/admin/custom/route.ts" highlights={[["17", "req.auth_context.actor_id", "Access the logged-in admin user's ID."]]} collapsibleLines="1-7" expandButtonLabel="Show Imports" import type { AuthenticatedMedusaRequest, MedusaResponse, @@ -125,12 +128,12 @@ For example: export const highlights = [ [ - "11", + "7", "authenticate", "Only authenticated admin users can access routes starting with `/custom/admin`", ], [ - "17", + "11", "authenticate", "Only authenticated customers can access routes starting with `/custom/customers`", ], diff --git a/www/apps/book/app/advanced-development/data-models/manage-relationships/page.mdx b/www/apps/book/app/advanced-development/data-models/manage-relationships/page.mdx index cd2864e77b..bb6fb6b90c 100644 --- a/www/apps/book/app/advanced-development/data-models/manage-relationships/page.mdx +++ b/www/apps/book/app/advanced-development/data-models/manage-relationships/page.mdx @@ -27,14 +27,14 @@ export const belongsHighlights = [ // when creating an email const email = await helloModuleService.createEmail({ // other properties... - user_id: "123" + user_id: "123", }) // when updating an email const email = await helloModuleService.updateEmail({ id: "321", // other properties... - user_id: "123" + user_id: "123", }) ``` @@ -57,14 +57,14 @@ export const manyHighlights = [ // when creating a product const product = await helloModuleService.createProduct({ // other properties... - order_ids: ["123", "321"] + order_ids: ["123", "321"], }) // when updating an order const order = await helloModuleService.updateOrder({ id: "321", // other properties... - product_ids: ["123", "321"] + product_ids: ["123", "321"], }) ``` diff --git a/www/apps/book/app/advanced-development/data-models/write-migration/page.mdx b/www/apps/book/app/advanced-development/data-models/write-migration/page.mdx new file mode 100644 index 0000000000..37330869c8 --- /dev/null +++ b/www/apps/book/app/advanced-development/data-models/write-migration/page.mdx @@ -0,0 +1,58 @@ +export const metadata = { + title: `${pageNumber} Write Migration`, +} + +# {metadata.title} + +In this chapter, you'll learn how to create a migration and write it manually. + +## What is a Migration? + +A migration is a class created in a TypeScript or JavaScript file under a module's `migrations` directory. It has two methods: + +- The `up` method reflects changes on the database. +- The `down` method reverts the changes made in the `up` method. + +--- + +## How to Write a Migration? + +The Medusa CLI tool provides a [migrations generate](!resources!/medusa-cli#migrations-generate) command to generate a migration for the specified modules' data models. + +Alternatively, you can manually create a migration file under the `migrations` directory of your module. + +For example: + +```ts title="src/modules/hello/migrations/Migration20240429.ts" +import { Migration } from "@mikro-orm/migrations" + +export class Migration20240702105919 extends Migration { + + async up(): Promise { + this.addSql("create table if not exists \"my_custom\" (\"id\" text not null, \"name\" text not null, \"created_at\" timestamptz not null default now(), \"updated_at\" timestamptz not null default now(), \"deleted_at\" timestamptz null, constraint \"my_custom_pkey\" primary key (\"id\"));") + } + + async down(): Promise { + this.addSql("drop table if exists \"my_custom\" cascade;") + } + +} +``` + +The migration's file name should be of the format `Migration{YEAR}{MONTH}{DAY}.ts`. The migration class in the file extends the `Migration` class imported from `@mikro-orm/migrations`. + +In the `up` and `down` method of the migration class, you use the `addSql` method provided by MikroORM's `Migration` class to run PostgreSQL syntax. + +In the example above, the `up` method creates the table `my_custom`, and the `down` method drops the table if the migration is reverted. + +--- + +## Run the Migration + +To run your migration, run the following command: + +```bash +npx medusa migrations run +``` + +This reflects the changes in the database as implemented in the migration's `up` method. diff --git a/www/apps/book/app/advanced-development/modules/module-links/page.mdx b/www/apps/book/app/advanced-development/modules/module-links/page.mdx index ef0f5dead8..d038997355 100644 --- a/www/apps/book/app/advanced-development/modules/module-links/page.mdx +++ b/www/apps/book/app/advanced-development/modules/module-links/page.mdx @@ -111,7 +111,7 @@ import { defineLink } from "@medusajs/utils" export default defineLink( { linkable: HelloModule.linkable.myCustom, - isList: true + isList: true, }, ProductModule.linkable.product ) diff --git a/www/apps/book/app/advanced-development/modules/remote-query/page.mdx b/www/apps/book/app/advanced-development/modules/remote-query/page.mdx index 296d163eae..e9760fa4ba 100644 --- a/www/apps/book/app/advanced-development/modules/remote-query/page.mdx +++ b/www/apps/book/app/advanced-development/modules/remote-query/page.mdx @@ -89,7 +89,7 @@ const query = remoteQueryObjectFromString({ fields: [ "id", "name", - "product.*" + "product.*", ], }) ``` @@ -112,7 +112,7 @@ const query = remoteQueryObjectFromString({ fields: [ "id", "name", - "products.*" + "products.*", ], }) ``` diff --git a/www/apps/book/app/advanced-development/workflows/advanced-example/page.mdx b/www/apps/book/app/advanced-development/workflows/advanced-example/page.mdx index 595893ed9b..070d67f8b3 100644 --- a/www/apps/book/app/advanced-development/workflows/advanced-example/page.mdx +++ b/www/apps/book/app/advanced-development/workflows/advanced-example/page.mdx @@ -40,10 +40,10 @@ The first step in the workflow receives the product’s ID and the data to updat Create the file `src/workflows/update-product-erp/steps/update-product.ts` with the following content: export const updateProductHighlights = [ - ["13", "resolve", "Resolve the `ProductService` from the Medusa container."], - ["16", "previousProductData", "Retrieve the `previousProductData` to pass it to the compensation function."], - ["19", "updateProducts", "Update the product."], - ["39", "updateProducts", "Revert the product’s data using the `previousProductData` passed from the step to the compensation function."] + ["10", "resolve", "Resolve the `ProductService` from the Medusa container."], + ["13", "previousProductData", "Retrieve the `previousProductData` to pass it to the compensation function."], + ["16", "updateProducts", "Update the product."], + ["30", "updateProducts", "Revert the product’s data using the `previousProductData` passed from the step to the compensation function."] ] ```ts title="src/workflows/update-product-erp/steps/update-product.ts" highlights={updateProductHighlights} collapsibleLines="1-9" expandButtonLabel="Show Imports" @@ -130,22 +130,22 @@ Create the file `src/workflows/update-product-erp/steps/update-erp.ts` with the export const updateErpHighlights = [ [ - "12", + "9", "resolve", "Resolve the `erpModuleService` from the Medusa container.", ], [ - "17", + "14", "previousErpData", "Retrieve the `previousErpData` to pass it to the compensation function.", ], [ - "21", + "16", "updateProductErpData", "Update the product’s ERP data and return the data from the ERP system.", ], [ - "37", + "31", "updateProductErpData", "Revert the product's data in the ERP system to its previous state using the `previousErpData`.", ], diff --git a/www/apps/book/app/advanced-development/workflows/conditions/page.mdx b/www/apps/book/app/advanced-development/workflows/conditions/page.mdx index 2d4939f6bc..81a60ee519 100644 --- a/www/apps/book/app/advanced-development/workflows/conditions/page.mdx +++ b/www/apps/book/app/advanced-development/workflows/conditions/page.mdx @@ -27,7 +27,7 @@ export const highlights = [ ```ts highlights={highlights} import { createWorkflow, - when + when, } from "@medusajs/workflows-sdk" // step imports... diff --git a/www/apps/book/app/advanced-development/workflows/constructor-constraints/page.mdx b/www/apps/book/app/advanced-development/workflows/constructor-constraints/page.mdx index ccd6bb59be..b8088f728d 100644 --- a/www/apps/book/app/advanced-development/workflows/constructor-constraints/page.mdx +++ b/www/apps/book/app/advanced-development/workflows/constructor-constraints/page.mdx @@ -127,7 +127,7 @@ const step1 = createStep( // ... return new StepResponse({ - myMap + myMap, }) } ) @@ -146,7 +146,7 @@ const step1 = createStep( // ... return new StepResponse({ - myObj + myObj, }) } ) diff --git a/www/apps/book/app/basics/commerce-modules/page.mdx b/www/apps/book/app/basics/commerce-modules/page.mdx index b75d2b07fd..90865c42e1 100644 --- a/www/apps/book/app/basics/commerce-modules/page.mdx +++ b/www/apps/book/app/basics/commerce-modules/page.mdx @@ -22,17 +22,21 @@ Similarly to your custom module, a commerce module's main service is registered For example, you saw this code snippet in the [Medusa container chapter](../medusa-container/page.mdx): -```ts highlights={[["13"]]} +```ts highlights={[["10"]]} import type { MedusaRequest, MedusaResponse } from "@medusajs/medusa" import { IProductModuleService } from "@medusajs/types" import { ModuleRegistrationName } from "@medusajs/utils" -export const GET = async (req: MedusaRequest, res: MedusaResponse) => { +export const GET = async ( + req: MedusaRequest, + res: MedusaResponse +) => { const productModuleService: IProductModuleService = req.scope.resolve( ModuleRegistrationName.PRODUCT ) - const [, count] = await productModuleService.listAndCount() + const [, count] = await productModuleService + .listAndCountProducts() res.json({ count, diff --git a/www/apps/book/app/basics/data-models/page.mdx b/www/apps/book/app/basics/data-models/page.mdx index 9eff644021..103f1facd8 100644 --- a/www/apps/book/app/basics/data-models/page.mdx +++ b/www/apps/book/app/basics/data-models/page.mdx @@ -17,7 +17,7 @@ A data model is a class that represents a table in the database. It's created in 1. Create a data model in a module. -2. Create migration for the data model. +2. Generate migration for the data model. 4. Run migration to add the data model's table in the database. @@ -44,53 +44,21 @@ You define a data model using the `model`'s `define` method. It accepts two para The example above defines the data model `MyCustom` with the properties `id` and `name`. -### Create a Migration +### Generate a Migration A migration defines changes to be made in the database, such as create or update tables. -A migration is a class created in a TypeScript or JavaScript file under a module's `migrations` directory. It has two methods: +To generate a migration for the data models in your module, run the following command: -- The `up` method reflects changes on the database. -- The `down` method reverts the changes made in the `up` method. +```bash +npx medusa migrations generate helloModuleService +``` -
- To generate migrations: +The `migrations generate` command of the Medusa CLI accepts one or more module names (for example, `helloModuleService`) to generate the migration for. - 1. Create the file `src/modules/hello/migrations-config.ts` with the following content: +The above command creates a migration file at the directory `src/modules/hello/migrations` similar to the following: - ```ts - import { defineMikroOrmCliConfig } from "@medusajs/utils" - import path from "path" - import MyCustom from "./models/my-custom" - import { HELLO_MODULE } from "." - - export default defineMikroOrmCliConfig(HELLO_MODULE, { - entities: [MyCustom] as any[], - migrations: { - path: path.join(__dirname, "migrations"), - }, - }) - ``` - - 2. Run the following command in the root directory of your Medusa application: - - ```bash - npx cross-env MIKRO_ORM_CLI=./src/modules/hello/migrations-config.ts mikro-orm migration:create - ``` - - - - Add this command as a script in `package.json` for easy usage in the future. Use this command whenever you want to generate a new migration in your module. - - - - After running the command, a migration file is generated under the `src/modules/hello/migrations` directory. - -
- -For example: - -```ts title="src/modules/migrations/Migration20240429090012.ts" +```ts import { Migration } from "@mikro-orm/migrations" export class Migration20240702105919 extends Migration { @@ -106,17 +74,11 @@ export class Migration20240702105919 extends Migration { } ``` -In the `up` method, you create the table `my_custom` and define its columns. In the `down` method, you drop the table. - - - -The queries performed in each of the methods use PostgreSQL syntax. - - +In the migration class, the `up` method creates the table `my_custom` and defines its columns. The `down` method drops the table. ### Run Migration -To reflect the changes in the migration, run the `migration` command: +To reflect the changes in the generated migration file, run the `migration` command: ```bash npx medusa migrations run diff --git a/www/apps/book/app/basics/events-and-subscribers/page.mdx b/www/apps/book/app/basics/events-and-subscribers/page.mdx index 3a641af9ff..b4c6fe8bd0 100644 --- a/www/apps/book/app/basics/events-and-subscribers/page.mdx +++ b/www/apps/book/app/basics/events-and-subscribers/page.mdx @@ -81,9 +81,9 @@ The subscriber function accepts an object parameter with the property `container For example: export const highlights = [ - ["10", "container", "Recieve the Medusa Container in the object parameter."], - ["13", "resolve", "Resolve the Product Module's main service."], - ["13", "ModuleRegistrationName.PRODUCT", "The module's registration name imported from `@medusajs/modules-sdk`."] + ["7", "container", "Recieve the Medusa Container in the object parameter."], + ["10", "resolve", "Resolve the Product Module's main service."], + ["10", "ModuleRegistrationName.PRODUCT", "The module's registration name imported from `@medusajs/modules-sdk`."] ] ```ts title="src/subscribers/product-created.ts" highlights={highlights} diff --git a/www/apps/book/app/basics/medusa-container/page.mdx b/www/apps/book/app/basics/medusa-container/page.mdx index d28e7a8a01..cede456b46 100644 --- a/www/apps/book/app/basics/medusa-container/page.mdx +++ b/www/apps/book/app/basics/medusa-container/page.mdx @@ -15,9 +15,9 @@ You use the Medusa container to resolve resources, such as services. For example, in a custom API route you can resolve any service registered in the Medusa application using the `scope.resolve` method of the `MedusaRequest` parameter: export const highlights = [ - ["13", "resolve", "Resolve the Product Module's main service."], + ["9", "resolve", "Resolve the Product Module's main service."], [ - "13", + "10", "ModuleRegistrationName.PRODUCT", "The resource registration name imported from `@medusajs/modules-sdk`.", ], @@ -28,7 +28,10 @@ import type { MedusaRequest, MedusaResponse } from "@medusajs/medusa" import { IProductModuleService } from "@medusajs/types" import { ModuleRegistrationName } from "@medusajs/utils" -export const GET = async (req: MedusaRequest, res: MedusaResponse) => { +export const GET = async ( + req: MedusaRequest, + res: MedusaResponse +) => { const productModuleService: IProductModuleService = req.scope.resolve( ModuleRegistrationName.PRODUCT ) diff --git a/www/apps/book/app/basics/modules-and-services/page.mdx b/www/apps/book/app/basics/modules-and-services/page.mdx index f370a383ed..a91332ea57 100644 --- a/www/apps/book/app/basics/modules-and-services/page.mdx +++ b/www/apps/book/app/basics/modules-and-services/page.mdx @@ -70,21 +70,18 @@ The last step is to add the module in Medusa’s configurations. In `medusa-config.js`, add a `modules` property and pass to it your custom module: -```js title="medusa-config.js" highlights={[["7", "HELLO_MODULE", "The key of the main service to be registered in the Medusa container."]]} -import { HELLO_MODULE } from "./src/modules/hello" -// ... - +```js title="medusa-config.js" highlights={[["4", "helloModuleService", "The key of the main service to be registered in the Medusa container."]]} module.exports = defineConfig({ // ... modules: { - [HELLO_MODULE]: { + "helloModuleService": { resolve: "./modules/hello", }, }, }) ``` -Its key (`helloModuleService` or `HELLO_MODULE`) is the name of the module’s main service. It will be registered in the Medusa container with that name. It should also be the same name passed as the first parameter to the `Module` function in the module's definition. +Its key (`helloModuleService`) is the name of the module’s main service. It will be registered in the Medusa container with that name. It should also be the same name passed as the first parameter to the `Module` function in the module's definition. Its value is an object having the `resolve` property, whose value is either a path to module's directory relative to `src`(it shouldn't include `src` in the path), or an `npm` package’s name. diff --git a/www/apps/book/sidebar.mjs b/www/apps/book/sidebar.mjs index 81f39e41eb..f30e0ace14 100644 --- a/www/apps/book/sidebar.mjs +++ b/www/apps/book/sidebar.mjs @@ -164,6 +164,10 @@ export const sidebar = sidebarAttachHrefCommonOptions( path: "/advanced-development/data-models/searchable-property", title: "Searchable Property", }, + { + path: "/advanced-development/data-models/write-migration", + title: "Write Migration", + }, ], }, { diff --git a/www/apps/resources/app/commerce-modules/order/page.mdx b/www/apps/resources/app/commerce-modules/order/page.mdx index 9289451192..d3be76aab9 100644 --- a/www/apps/resources/app/commerce-modules/order/page.mdx +++ b/www/apps/resources/app/commerce-modules/order/page.mdx @@ -74,7 +74,7 @@ const orderReturn = await orderModuleService.createReturn({ items: [{ id: "orditem_123", quantity: 1, - }] + }], }) ``` diff --git a/www/apps/resources/app/commerce-modules/order/promotion-adjustments/page.mdx b/www/apps/resources/app/commerce-modules/order/promotion-adjustments/page.mdx index ae6dfcd62a..be72e65dd0 100644 --- a/www/apps/resources/app/commerce-modules/order/promotion-adjustments/page.mdx +++ b/www/apps/resources/app/commerce-modules/order/promotion-adjustments/page.mdx @@ -102,7 +102,7 @@ const actions = await promotionModuleService.computeActions( items: lineItemAdjustments, shipping_methods: shippingMethodAdjustments, // TODO infer from cart or region - currency_code: "usd" + currency_code: "usd", } ) ``` diff --git a/www/apps/resources/app/medusa-cli/page.mdx b/www/apps/resources/app/medusa-cli/page.mdx index 2882764f0a..d4faaa20dc 100644 --- a/www/apps/resources/app/medusa-cli/page.mdx +++ b/www/apps/resources/app/medusa-cli/page.mdx @@ -8,12 +8,6 @@ export const metadata = { The Medusa CLI tool provides commands that facilitate your development. - - -Medusa's CLI tool doesn't fully support Medusa v2 yet. - - - - [Node.js v16+](https://nodejs.org/en/download) @@ -23,19 +17,14 @@ Medusa's CLI tool doesn't fully support Medusa v2 yet. ## Usage -In your Medusa application's directory, you can use the Medusa CLI tool using NPX. For example: +In your Medusa application's directory, you can use the Medusa CLI tool using NPX. + +For example: ```bash npx medusa --help ``` -Alternatively, you can install the CLI tool globally: - -```bash npm2yarn -npm install @medusajs/medusa-cli -g -medusa --help -``` - --- ## Commands @@ -348,7 +337,7 @@ Run the latest migrations to reflect changes on the database. npx medusa migrations run ``` -### migration revert +### migrations revert Revert the last migrations ran on one or more modules. @@ -375,7 +364,46 @@ npx medusa migrations revert - The name of one or more module, separated by spaces. For example, `helloModuleService`. + The name of one or more module (separated by spaces) to rever their migrations. For example, `helloModuleService`. + + + + + Yes + + + + + + +### migrations generate + +Generate a migration file for the latest changes in one or more modules. + +```bash +npx medusa migrations generate +``` + +#### Arguments + + + + + Argument + Description + Required + + + + + + + `module_names` + + + + + The name of one or more module (separated by spaces) to generate migrations for. For example, `helloModuleService`. diff --git a/www/apps/resources/app/recipes/marketplace/examples/vendors/page.mdx b/www/apps/resources/app/recipes/marketplace/examples/vendors/page.mdx index 15efaeaa17..2cbb29c784 100644 --- a/www/apps/resources/app/recipes/marketplace/examples/vendors/page.mdx +++ b/www/apps/resources/app/recipes/marketplace/examples/vendors/page.mdx @@ -67,7 +67,7 @@ const Vendor = model.define("vendor", { handle: model.text(), name: model.text(), logo: model.text().nullable(), - admins: model.hasMany(() => VendorAdmin) + admins: model.hasMany(() => VendorAdmin), }) export default Vendor @@ -89,8 +89,8 @@ const VendorAdmin = model.define("vendor_admin", { last_name: model.text().nullable(), email: model.text().unique(), vendor: model.belongsTo(() => Vendor, { - mappedBy: "admins" - }) + mappedBy: "admins", + }), }) export default VendorAdmin @@ -98,34 +98,6 @@ export default VendorAdmin This creates a `VendorAdmin` data model, which represents an admin of a vendor. -### Generate Migrations - -To create tables for the data models in the database, you need to generate migrations for them. - -Create the file `src/modules/marketplace/migrations-config.ts` with the following content: - -```ts -import { defineMikroOrmCliConfig } from "@medusajs/utils" -import path from "path" -import Vendor from "./models/vendor" -import VendorAdmin from "./models/vendor-admin" - -export default defineMikroOrmCliConfig("marketplace", { - entities: [Vendor, VendorAdmin] as any[], - migrations: { - path: path.join(__dirname, "migrations"), - }, -}) -``` - -Then, run the following command to generate the migration: - -```bash -npx cross-env MIKRO_ORM_CLI=./src/modules/marketplace/migrations-config.ts mikro-orm migration:create -``` - -This generates a migration in the `src/modules/marketeplace/migrations` directory. - ### Create Main Module Service Next, create the main service of the module at `src/modules/marketplace/service.ts` with the following content: @@ -138,7 +110,7 @@ import { CreateVendorData, VendorData } from "./types" class MarketplaceModuleService extends MedusaService({ Vendor, - VendorAdmin + VendorAdmin, }) { } @@ -158,7 +130,7 @@ import MarketplaceModuleService from "./service" export const MARKETPLACE_MODULE = "marketplaceModuleService" export default Module(MARKETPLACE_MODULE, { - service: MarketplaceModuleService + service: MarketplaceModuleService, }) ``` @@ -167,7 +139,7 @@ export default Module(MARKETPLACE_MODULE, { Finally, add the module to the list of modules in `medusa-config.js`: ```ts title="medusa-config.js" -import { MARKETPLACE_MODULE } from './src/modules/marketplace' +import { MARKETPLACE_MODULE } from "./src/modules/marketplace" // ... @@ -177,10 +149,10 @@ module.exports = defineConfig({ [MARKETPLACE_MODULE]: { resolve: "./modules/marketplace", definition: { - isQueryable: true - } - } - } + isQueryable: true, + }, + }, + }, }) ``` @@ -212,7 +184,7 @@ export default defineLink( MarketplaceModule.linkable.vendor, { linkable: ProductModule.linkable.product, - isList: true + isList: true, } ) ``` @@ -230,7 +202,7 @@ export default defineLink( MarketplaceModule.linkable.vendor, { linkable: OrderModule.linkable.order, - isList: true + isList: true, } ) ``` @@ -243,9 +215,17 @@ This adds a list link between the `Vendor` and `Order` data models, indicating t --- -## Step 3: Run Migrations +## Step 3: Generate and Run Migrations -To reflect the module’s data models and the module links in the database, run the following command: +To create tables for the marketplace data models in the database, start by generating the migrations for the Marketplace Module with the following command: + +```bash +npx medusa migrations generate marketplaceModuleService +``` + +This generates a migration in the `src/modules/marketeplace/migrations` directory. + +Then, to reflect the migration and the module links in the database, run the following command: ```bash npx medusa migrations run @@ -372,12 +352,12 @@ export const vendorRouteSchemaHighlights = [ ```ts title="src/api/vendors/route.ts" highlights={vendorRouteSchemaHighlights} import { AuthenticatedMedusaRequest, - MedusaResponse + MedusaResponse, } from "@medusajs/medusa" import { MedusaError } from "@medusajs/utils" import { z } from "zod" -import MarketplaceModuleService from "../../modules/marketplace/service"; -import createVendorAdminWorkflow from "../../workflows/marketplace/create-vendor-admin"; +import MarketplaceModuleService from "../../modules/marketplace/service" +import createVendorAdminWorkflow from "../../workflows/marketplace/create-vendor-admin" const schema = z.object({ name: z.string(), @@ -386,8 +366,8 @@ const schema = z.object({ admin: z.object({ email: z.string(), first_name: z.string().optional(), - last_name: z.string().optional() - }).strict() + last_name: z.string().optional(), + }).strict(), }).strict() type RequestBody = { @@ -441,15 +421,15 @@ export const POST = async ( input: { admin: { ...admin, - vendor_id: vendor[0].id + vendor_id: vendor[0].id, }, authIdentityId: req.auth_context.auth_identity_id, - } + }, }) // retrieve vendor again with admins vendor = await marketplaceModuleService.retrieveVendor(vendor[0].id, { - relations: ["admins"] + relations: ["admins"], }) res.json({ @@ -482,7 +462,7 @@ export const config: MiddlewaresConfig = { matcher: "/vendors/*", middlewares: [ authenticate("vendor", ["session", "bearer"]), - ] + ], }, ], } @@ -571,12 +551,12 @@ export const retrieveProductHighlights = [ ] ```ts title="src/api/vendors/products/route.ts" highlights={retrieveProductHighlights} -import { AuthenticatedMedusaRequest, MedusaResponse } from "@medusajs/medusa"; +import { AuthenticatedMedusaRequest, MedusaResponse } from "@medusajs/medusa" import { remoteQueryObjectFromString, } from "@medusajs/utils" -import MarketplaceModuleService from "../../../modules/marketplace/service"; -import { MARKETPLACE_MODULE } from "../../../modules/marketplace"; +import MarketplaceModuleService from "../../../modules/marketplace/service" +import { MARKETPLACE_MODULE } from "../../../modules/marketplace" export const GET = async ( req: AuthenticatedMedusaRequest, @@ -589,7 +569,7 @@ export const GET = async ( const vendorAdmin = await marketplaceModuleService.retrieveVendorAdmin( req.auth_context.actor_id, { - relations: ["vendor"] + relations: ["vendor"], } ) @@ -598,15 +578,15 @@ export const GET = async ( fields: ["products.*"], variables: { filters: { - id: [vendorAdmin.vendor.id] - } - } + id: [vendorAdmin.vendor.id], + }, + }, }) const result = await remoteQuery(query) res.json({ - products: result[0].products + products: result[0].products, }) } ``` @@ -626,11 +606,11 @@ import { createProductsWorkflow } from "@medusajs/core-flows" import { CreateProductWorkflowInputDTO, IProductModuleService, - ISalesChannelModuleService + ISalesChannelModuleService, } from "@medusajs/types" import { Modules, - ModuleRegistrationName + ModuleRegistrationName, } from "@medusajs/utils" // GET method... @@ -655,7 +635,7 @@ export const POST = async ( const vendorAdmin = await marketplaceModuleService.retrieveVendorAdmin( req.auth_context.actor_id, { - relations: ["vendor"] + relations: ["vendor"], } ) @@ -678,19 +658,19 @@ const { result } = await createProductsWorkflow(req.scope) input: { products: [{ ...req.body, - sales_channels: salesChannels - }] - } + sales_channels: salesChannels, + }], + }, }) // link product to vendor await remoteLink.create({ [MARKETPLACE_MODULE]: { - vendor_id: vendorAdmin.vendor.id + vendor_id: vendorAdmin.vendor.id, }, [Modules.PRODUCT]: { - product_id: result[0].id - } + product_id: result[0].id, + }, }) // retrieve product again @@ -699,7 +679,7 @@ const product = await productModuleService.retrieveProduct( ) res.json({ - product + product, }) ``` @@ -727,8 +707,8 @@ export const config: MiddlewaresConfig = { middlewares: [ authenticate("vendor", ["session", "bearer"]), validateAndTransformBody(AdminCreateProduct), - ] - } + ], + }, ], } ``` @@ -823,11 +803,11 @@ const retrieveCartStep = createStep( .resolve(ModuleRegistrationName.CART) const cart = await cartModuleService.retrieveCart(cart_id, { - relations: ["items"] + relations: ["items"], }) return new StepResponse({ - cart + cart, }) } ) @@ -862,12 +842,12 @@ const createParentOrderStep = createStep( const { result } = await completeCartWorkflow(container) .run({ input: { - id: cart_id - } + id: cart_id, + }, }) return new StepResponse({ - order: result + order: result, }) } ) @@ -906,9 +886,9 @@ const groupVendorItemsStep = createStep( fields: ["vendor.*"], variables: { filters: { - id: [item.product_id] - } - } + id: [item.product_id], + }, + }, }) const result = await remoteQuery(query) @@ -920,12 +900,12 @@ const groupVendorItemsStep = createStep( } vendorsItems[vendorId] = [ ...(vendorsItems[vendorId] || []), - item + item, ] })) return new StepResponse({ - vendorsItems + vendorsItems, }) } ) @@ -973,7 +953,7 @@ const createVendorOrdersStep = createStep( const vendorIds = Object.keys(vendorsItems) if (vendorIds.length === 0) { return new StepResponse({ - orders: [] + orders: [], }) } const remoteLink = container.resolve("remoteLink") @@ -1005,20 +985,20 @@ if (isOnlyOneVendorOrder) { // link the parent order to the vendor instead of creating child orders await remoteLink.create({ [MARKETPLACE_MODULE]: { - vendor_id: vendorId + vendor_id: vendorId, }, [Modules.ORDER]: { - order_id: parentOrder.id - } + order_id: parentOrder.id, + }, }) return new StepResponse({ orders: [ { ...parentOrder, - vendor - } - ] + vendor, + }, + ], }) } @@ -1050,7 +1030,7 @@ await Promise.all( input: { items, metadata: { - parent_order_id: parentOrder.id + parent_order_id: parentOrder.id, }, // use info from parent region_id: parentOrder.region_id, @@ -1074,37 +1054,37 @@ await Promise.all( rate: taxLine.rate, provider_id: taxLine.provider_id, tax_rate_id: taxLine.tax_rate_id, - description: taxLine.description + description: taxLine.description, })), adjustments: shippingMethod.adjustments.map((adjustment) => ({ code: adjustment.code, amount: adjustment.amount, description: adjustment.description, promotion_id: adjustment.promotion_id, - provider_id: adjustment.provider_id - })) + provider_id: adjustment.provider_id, + })), })), - } + }, }) await remoteLink.create({ [MARKETPLACE_MODULE]: { - vendor_id: vendorId + vendor_id: vendorId, }, [Modules.ORDER]: { - order_id: childOrder.id - } + order_id: childOrder.id, + }, }) createdOrders.push({ ...childOrder, - vendor + vendor, }) }) ) return new StepResponse({ - orders: createdOrders + orders: createdOrders, }) ``` @@ -1150,7 +1130,7 @@ const createVendorOrdersWorkflow = createWorkflow< const { vendorsItems } = groupVendorItemsStep( transform({ - cart + cart, }, (data) => data ) @@ -1159,12 +1139,12 @@ const createVendorOrdersWorkflow = createWorkflow< const { orders } = createVendorOrdersStep( transform({ order, - vendorsItems + vendorsItems, }, (data) => { return { parentOrder: data.order, - vendorsItems: data.vendorsItems + vendorsItems: data.vendorsItems, } } ) @@ -1172,11 +1152,11 @@ const createVendorOrdersWorkflow = createWorkflow< return transform({ order, - orders + orders, }, (data) => ({ parent_order: data.order, - vendor_orders: data.orders + vendor_orders: data.orders, }) ) } @@ -1196,9 +1176,9 @@ Create the file `src/api/store/carts/[id]/complete/route.ts` with the following ```ts title="src/api/store/carts/[id]/complete/route.ts" import { AuthenticatedMedusaRequest, - MedusaResponse -} from "@medusajs/medusa"; -import createVendorOrdersWorkflow from "../../../../../workflows/marketplace/create-vendor-orders"; + MedusaResponse, +} from "@medusajs/medusa" +import createVendorOrdersWorkflow from "../../../../../workflows/marketplace/create-vendor-orders" export const POST = async ( req: AuthenticatedMedusaRequest, @@ -1209,13 +1189,13 @@ export const POST = async ( const { result } = await createVendorOrdersWorkflow(req.scope) .run({ input: { - cart_id: cartId - } + cart_id: cartId, + }, }) res.json({ type: "order", - order: result.parent_order + order: result.parent_order, }) } ``` @@ -1241,11 +1221,11 @@ export const getOrderHighlights = [ ] ```ts title="src/api/vendors/orders/route.ts" highlights={getOrderHighlights} collapsibleLines="1-6" expandMoreLabel="Show Imports" -import { AuthenticatedMedusaRequest, MedusaResponse } from "@medusajs/medusa"; +import { AuthenticatedMedusaRequest, MedusaResponse } from "@medusajs/medusa" import { remoteQueryObjectFromString } from "@medusajs/utils" import { getOrdersListWorkflow } from "@medusajs/core-flows" -import MarketplaceModuleService from "../../../modules/marketplace/service"; -import { MARKETPLACE_MODULE } from "../../../modules/marketplace"; +import MarketplaceModuleService from "../../../modules/marketplace/service" +import { MARKETPLACE_MODULE } from "../../../modules/marketplace" export const GET = async ( req: AuthenticatedMedusaRequest, @@ -1258,7 +1238,7 @@ export const GET = async ( const vendorAdmin = await marketplaceModuleService.retrieveVendorAdmin( req.auth_context.actor_id, { - relations: ["vendor"] + relations: ["vendor"], } ) @@ -1267,9 +1247,9 @@ export const GET = async ( fields: ["orders.*"], variables: { filters: { - id: [vendorAdmin.vendor.id] - } - } + id: [vendorAdmin.vendor.id], + }, + }, }) const result = await remoteQuery(query) @@ -1295,14 +1275,14 @@ export const GET = async ( ], variables: { filters: { - id: result[0].orders.map((order) => order.id) - } - } - } + id: result[0].orders.map((order) => order.id), + }, + }, + }, }) res.json({ - orders + orders, }) } ``` @@ -1343,7 +1323,7 @@ export const config: MiddlewaresConfig = { authenticate("vendor", ["session", "bearer"]), ], }, - ] + ], } ```