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
This commit is contained in:
Shahed Nasser
2024-07-15 17:46:10 +02:00
committed by GitHub
parent 43eb38c8cb
commit a74c900ab1
19 changed files with 270 additions and 229 deletions
@@ -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 customers 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 users 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`",
],
@@ -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"],
})
```
@@ -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<void> {
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<void> {
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.
@@ -111,7 +111,7 @@ import { defineLink } from "@medusajs/utils"
export default defineLink(
{
linkable: HelloModule.linkable.myCustom,
isList: true
isList: true,
},
ProductModule.linkable.product
)
@@ -89,7 +89,7 @@ const query = remoteQueryObjectFromString({
fields: [
"id",
"name",
"product.*"
"product.*",
],
})
```
@@ -112,7 +112,7 @@ const query = remoteQueryObjectFromString({
fields: [
"id",
"name",
"products.*"
"products.*",
],
})
```
@@ -40,10 +40,10 @@ The first step in the workflow receives the products 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 products 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 products 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 products 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`.",
],
@@ -27,7 +27,7 @@ export const highlights = [
```ts highlights={highlights}
import {
createWorkflow,
when
when,
} from "@medusajs/workflows-sdk"
// step imports...
@@ -127,7 +127,7 @@ const step1 = createStep(
// ...
return new StepResponse({
myMap
myMap,
})
}
)
@@ -146,7 +146,7 @@ const step1 = createStep(
// ...
return new StepResponse({
myObj
myObj,
})
}
)
@@ -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,
+11 -49
View File
@@ -17,7 +17,7 @@ A data model is a class that represents a table in the database. It's created in
<Note title="Steps Summary">
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.
</Note>
@@ -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
```
<Details summaryContent="Generate Migration">
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
```
<Note title="Tip">
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.
</Note>
After running the command, a migration file is generated under the `src/modules/hello/migrations` directory.
</Details>
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.
<Note title="Tip">
The queries performed in each of the methods use PostgreSQL syntax.
</Note>
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
@@ -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}
@@ -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
)
@@ -70,21 +70,18 @@ The last step is to add the module in Medusas 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 modules 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 modules 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` packages name.