docs: fixes and changes based on latest updates (#7322)
* docs: changes based on DX changes * remove fields no longer needed * remove unnecessary parameters * fixes to authenticate middleware usage * add highlight to migrations config * change configuration to http * added missing remote link docs * fix name in sidebar * added notification module docs + updated file module docs * add vale exceptions * fix vale errors * added docs on custom cli scripts
This commit is contained in:
@@ -12,16 +12,18 @@ Cross-Origin Resource Sharing (CORS) allows only configured origins to access yo
|
||||
|
||||
### CORS Configurations
|
||||
|
||||
You configure allowed origins for Store and Admin API Routes using the `store_cors` and `admin_cors` configurations in `medusa-config.js`. Each of these configurations accepts a URL pattern to identify allowed origins.
|
||||
You configure allowed origins for Store and Admin API Routes using the `storeCors` and `adminCors` properties of the `http` configuration in `medusa-config.js`. Each of these configurations accepts a URL pattern to identify allowed origins.
|
||||
|
||||
For example:
|
||||
|
||||
```js title="medusa-config.js"
|
||||
module.exports = {
|
||||
projectConfig: {
|
||||
admin_cors: "http://localhost:7001",
|
||||
store_cors: "http://localhost:8000",
|
||||
// ...
|
||||
http: {
|
||||
adminCors: "http://localhost:7001",
|
||||
storeCors: "http://localhost:8000",
|
||||
// ...
|
||||
}
|
||||
},
|
||||
// ...
|
||||
}
|
||||
@@ -90,7 +92,7 @@ export const config: MiddlewaresConfig = {
|
||||
|
||||
return cors({
|
||||
origin: parseCorsOrigins(
|
||||
configModule.projectConfig.store_cors
|
||||
configModule.projectConfig.http.storeCors
|
||||
),
|
||||
credentials: true,
|
||||
})(req, res, next)
|
||||
@@ -101,4 +103,4 @@ export const config: MiddlewaresConfig = {
|
||||
}
|
||||
```
|
||||
|
||||
This retrieves the configurations exported from `medusa-config.js` and applies the `store_cors` to routes starting with `/custom`.
|
||||
This retrieves the configurations exported from `medusa-config.js` and applies the `storeCors` to routes starting with `/custom`.
|
||||
|
||||
@@ -59,21 +59,21 @@ For example:
|
||||
|
||||
```ts title="src/api/store/me/custom/route.ts" highlights={[["16", "", "Access the logged-in customer's ID."]]}
|
||||
import type {
|
||||
MedusaRequest,
|
||||
AuthenticatedMedusaRequest,
|
||||
MedusaResponse,
|
||||
} from "@medusajs/medusa"
|
||||
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
|
||||
import { ICustomerModuleService } from "@medusajs/types"
|
||||
|
||||
export const GET = async (
|
||||
req: MedusaRequest,
|
||||
req: AuthenticatedMedusaRequest,
|
||||
res: MedusaResponse
|
||||
) => {
|
||||
const customerService: ICustomerModuleService =
|
||||
req.scope.resolve(ModuleRegistrationName.CUSTOMER)
|
||||
|
||||
const customer = await customerService.retrieve(
|
||||
req.user.customer_id
|
||||
req.auth.actor_id
|
||||
)
|
||||
|
||||
// ...
|
||||
@@ -92,21 +92,21 @@ For example:
|
||||
|
||||
```ts title="src/api/admin/custom/route.ts" highlights={[["16", "req.user.userId", "Access the logged-in admin user's ID."]]}
|
||||
import type {
|
||||
MedusaRequest,
|
||||
AuthenticatedMedusaRequest,
|
||||
MedusaResponse,
|
||||
} from "@medusajs/medusa"
|
||||
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
|
||||
import { IUserModuleService } from "@medusajs/types"
|
||||
|
||||
export const GET = async (
|
||||
req: MedusaRequest,
|
||||
req: AuthenticatedMedusaRequest,
|
||||
res: MedusaResponse
|
||||
) => {
|
||||
const userService: IUserModuleService = req.scope.resolve(
|
||||
ModuleRegistrationName.USER
|
||||
)
|
||||
|
||||
const user = await userService.retrieve(req.user.userId)
|
||||
const user = await userService.retrieve(req.auth.actor_id)
|
||||
|
||||
// ...
|
||||
}
|
||||
@@ -129,9 +129,7 @@ export const highlights = [
|
||||
|
||||
```ts title="src/api/middlewares.ts" highlights={highlights}
|
||||
// TODO update import
|
||||
import {
|
||||
authenticate,
|
||||
} from "@medusajs/medusa/dist/utils/authenticate-middleware"
|
||||
import { MiddlewaresConfig, authenticate } from "@medusajs/medusa"
|
||||
|
||||
export const config: MiddlewaresConfig = {
|
||||
routes: [
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
export const metadata = {
|
||||
title: `${pageNumber} Custom CLI Scripts`,
|
||||
}
|
||||
|
||||
# {metadata.title}
|
||||
|
||||
In this chapter, you'll learn how create and execute custom scripts from 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 as a CLI tool.
|
||||
|
||||
---
|
||||
|
||||
## 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.
|
||||
|
||||
For example, create the file `src/scripts/my-script.ts` with the following content:
|
||||
|
||||
```ts title="src/scripts/my-script.ts"
|
||||
import {
|
||||
ExecArgs,
|
||||
IProductModuleService
|
||||
} from "@medusajs/types"
|
||||
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
|
||||
|
||||
export default async function myScript ({
|
||||
container
|
||||
}: ExecArgs) {
|
||||
const productModuleService: IProductModuleService =
|
||||
container.resolve(ModuleRegistrationName.PRODUCT)
|
||||
|
||||
const [, count] = await productModuleService.listAndCount()
|
||||
|
||||
console.log(`You have ${count} product(s)`)
|
||||
}
|
||||
```
|
||||
|
||||
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.
|
||||
|
||||
---
|
||||
|
||||
## How to Run Custom CLI Script?
|
||||
|
||||
To run the custom CLI script, `build` your code then run the `exec` command:
|
||||
|
||||
```bash npm2yarn
|
||||
npm run build
|
||||
npx medusa exec ./dist/scripts/my-script.js
|
||||
```
|
||||
|
||||
Notice that you pass the path to the file in the `dist` directory.
|
||||
|
||||
---
|
||||
|
||||
## Custom CLI Script Arguments
|
||||
|
||||
Your script can accept arguments from the command line. Arguments are passed to the function's object parameter in the `args` property.
|
||||
|
||||
For example:
|
||||
|
||||
```ts
|
||||
import { ExecArgs } from "@medusajs/types"
|
||||
|
||||
export default async function myScript ({
|
||||
args
|
||||
}: ExecArgs) {
|
||||
console.log(`The arguments you passed: ${args}`)
|
||||
}
|
||||
```
|
||||
|
||||
Then, pass the arguments in the `exec` command after the file path:
|
||||
|
||||
```bash npm2yarn
|
||||
npm run build
|
||||
npx medusa exec ./dist/scripts/my-script.js arg1 arg2
|
||||
```
|
||||
@@ -37,17 +37,14 @@ The following example showcase a data model with common definitions:
|
||||
|
||||
```ts
|
||||
import {
|
||||
BeforeCreate,
|
||||
Entity,
|
||||
Enum,
|
||||
OneToOne,
|
||||
PrimaryKey,
|
||||
Property,
|
||||
} from "@mikro-orm/core"
|
||||
import {
|
||||
createPsqlIndexStatementHelper,
|
||||
generateEntityId,
|
||||
} from "@medusajs/utils"
|
||||
import { BaseEntity } from "@medusajs/utils"
|
||||
// assuming this is another implemented data model
|
||||
import ProductVariant from "./product-variant"
|
||||
|
||||
export enum MediaType {
|
||||
@@ -55,14 +52,8 @@ export enum MediaType {
|
||||
PREVIEW = "preview"
|
||||
}
|
||||
|
||||
const VariantIdIndex = createPsqlIndexStatementHelper({
|
||||
name: "IDX_product_media_variant_id",
|
||||
tableName: "product_media",
|
||||
columns: "variant_id",
|
||||
}).MikroORMIndex
|
||||
|
||||
@Entity()
|
||||
export default class ProductMedia {
|
||||
class ProductMedia extends BaseEntity {
|
||||
@PrimaryKey({ columnType: "text" })
|
||||
id: string
|
||||
|
||||
@@ -78,7 +69,6 @@ export default class ProductMedia {
|
||||
@Property({ columnType: "text" })
|
||||
mime_type: string
|
||||
|
||||
@VariantIdIndex()
|
||||
@Property({ columnType: "text" })
|
||||
variant_id: string
|
||||
|
||||
@@ -87,21 +77,13 @@ export default class ProductMedia {
|
||||
onDelete: "cascade",
|
||||
})
|
||||
variant: ProductVariant
|
||||
|
||||
@BeforeCreate()
|
||||
onCreate() {
|
||||
this.id = generateEntityId(this.id, "promed")
|
||||
}
|
||||
|
||||
@OnInit()
|
||||
OnInit() {
|
||||
this.id = generateEntityId(this.id, "promed")
|
||||
}
|
||||
}
|
||||
|
||||
export default ProductMedia
|
||||
```
|
||||
|
||||
In the example above:
|
||||
|
||||
- The `ProductMedia` data model has the columns `id`, `name`, `type`, `file_key`, `mime_type`, and `variant_id`.
|
||||
- The data model has an index on the `variant_id` column.
|
||||
- The data model has a one-to-one relation to the `ProductVariant` data model.
|
||||
- The data model has a one-to-one relation to a `ProductVariant` data model.
|
||||
|
||||
@@ -20,25 +20,28 @@ When retrieving or listing records of that data model, records having their `del
|
||||
|
||||
To create a soft-deletable model, first, add the following filter decorator to the data model class:
|
||||
|
||||
```ts title="src/module/hello/models/my-soft-deletable.ts" highlights={[["6"]]}
|
||||
```ts title="src/module/hello/models/my-soft-deletable.ts" highlights={[["7"]]}
|
||||
// other imports...
|
||||
import { Entity, Filter } from "@mikro-orm/core"
|
||||
import { DALUtils } from "@medusajs/utils"
|
||||
import { BaseEntity } from "@medusajs/utils"
|
||||
|
||||
@Entity()
|
||||
@Filter(DALUtils.mikroOrmSoftDeletableFilterOptions)
|
||||
export default class MySoftDeletable {
|
||||
class MySoftDeletable extends BaseEntity {
|
||||
// ...
|
||||
}
|
||||
|
||||
export default MySoftDeletable
|
||||
```
|
||||
|
||||
Then, add a `deleted_at` field to the data model:
|
||||
|
||||
```ts highlights={[["4"], ["5"]]}
|
||||
```ts highlights={[["7"], ["8"]]}
|
||||
// other imports...
|
||||
import { Property } from "@mikro-orm/core"
|
||||
|
||||
export default class MySoftDeletable {
|
||||
class MySoftDeletable extends BaseEntity {
|
||||
// ...
|
||||
|
||||
@Property({ columnType: "timestamptz", nullable: true })
|
||||
|
||||
@@ -1,48 +0,0 @@
|
||||
export const metadata = {
|
||||
title: `${pageNumber} Database Connection Loader`,
|
||||
}
|
||||
|
||||
# {metadata.title}
|
||||
|
||||
In this document, you’ll learn about how to load the database connection in your module.
|
||||
|
||||
## How to Add a Database Connection Loader?
|
||||
|
||||
To ensure that your module's database operations use the Medusa application's database connection, export a database connection loader in your module's definition.
|
||||
|
||||
Medusa provides a utility function that creates the database connection loader for you.
|
||||
|
||||
For example:
|
||||
|
||||
```ts title="src/modules/hello/index.ts"
|
||||
// other imports...
|
||||
import { ModulesSdkUtils } from "@medusajs/utils"
|
||||
import { MyCustom } from "./models/my-custom"
|
||||
|
||||
const pathToMigrations = __dirname + "/migrations"
|
||||
|
||||
// ...
|
||||
|
||||
const connectionLoader = ModulesSdkUtils
|
||||
.mikroOrmConnectionLoaderFactory({
|
||||
moduleName,
|
||||
moduleModels: [MyCustom],
|
||||
migrationsPath: pathToMigrations,
|
||||
})
|
||||
|
||||
export default {
|
||||
// ...
|
||||
loaders: [
|
||||
// ...
|
||||
connectionLoader,
|
||||
],
|
||||
}
|
||||
```
|
||||
|
||||
The `mikroOrmConnectionLoaderFactory` function accepts an object with the following properties:
|
||||
|
||||
- `moduleName`: The name of the module.
|
||||
- `moduleModules`: An array of data models in the module.
|
||||
- `migrationsPath`: The path to the migrations file.
|
||||
|
||||
You can now implement data-management methods as explained in the next chapters.
|
||||
@@ -14,63 +14,12 @@ So, resources in the module, such as services or loaders, can only resolve other
|
||||
|
||||
---
|
||||
|
||||
## The Container Loader
|
||||
## Resources Registered in the Module's Container
|
||||
|
||||
Medusa provides a utility function that creates a container loader. This loader takes care of registering resources in your container.
|
||||
Some resources registered in the module's container are:
|
||||
|
||||
For example:
|
||||
|
||||
export const highlights = [
|
||||
["11", "moduleContainerLoaderFactory", "Create the container loader."],
|
||||
["25", "", "Export the container loader."]
|
||||
]
|
||||
|
||||
```ts title="src/modules/hello/index.ts" highlights={highlights}
|
||||
// other imports...
|
||||
import HelloModuleService from "./service"
|
||||
import { MyCustom } from "./models/my-custom"
|
||||
import {
|
||||
ModulesSdkUtils,
|
||||
MikroOrmBaseRepository,
|
||||
} from "@medusajs/utils"
|
||||
|
||||
// ...
|
||||
|
||||
const containerLoader = ModulesSdkUtils.moduleContainerLoaderFactory({
|
||||
moduleModels: {
|
||||
MyCustom,
|
||||
},
|
||||
moduleRepositories: {
|
||||
BaseRepository: MikroOrmBaseRepository,
|
||||
},
|
||||
moduleServices: {
|
||||
HelloModuleService,
|
||||
},
|
||||
})
|
||||
|
||||
export default {
|
||||
// ...
|
||||
loaders: [containerLoader],
|
||||
}
|
||||
```
|
||||
|
||||
You create the container loader using the utility function `moduleContainerLoaderFactory` and export it in the module's definition.
|
||||
|
||||
### moduleContainerLoaderFactory Parameters
|
||||
|
||||
The `moduleContainerLoaderFactory` function accepts as a parameter an object with the following properties:
|
||||
|
||||
- `moduleModels`: An object where each key is a data model's name, and its value the data model class.
|
||||
- `moduleRepositories`: An object of the module's repositories. You import here the `MikroOrmBaseRepository` from `@medusajs/utils` and use it as the value of `BaseRepository`.
|
||||
- `moduleServices`: An object where each key is the service's registration name, and its value is the service class.
|
||||
|
||||
### Resources Registered by the Container Loader
|
||||
|
||||
The container loader registers in the module's container:
|
||||
|
||||
- The services passed in the `moduleServices` property.
|
||||
- The repositories provided in the `moduleRepositories`.
|
||||
- A generated service for each data model provided in `moduleModels`.
|
||||
- The module's main service.
|
||||
- A generated service for each data model in your module. The registration name is the camel-case data model name suffixed by `Service`. For example, `myCustomService`.
|
||||
|
||||

|
||||
|
||||
@@ -84,7 +33,7 @@ A service's constructor accepts as a first parameter an object used to resolve r
|
||||
|
||||
For example:
|
||||
|
||||
```ts
|
||||
```ts highlights={[["5"], ["12"]]}
|
||||
import { ModulesSdkTypes } from "@medusajs/types"
|
||||
import { MyCustom } from "./models/my-custom"
|
||||
|
||||
@@ -109,12 +58,11 @@ A loader function in a module accepts as a parameter an object having the proper
|
||||
|
||||
For example:
|
||||
|
||||
```ts
|
||||
```ts highlights={[["8"]]}
|
||||
import {
|
||||
LoaderOptions,
|
||||
} from "@medusajs/modules-sdk"
|
||||
|
||||
|
||||
export default function helloWorldLoader({
|
||||
container,
|
||||
}: LoaderOptions) {
|
||||
|
||||
+4
-5
@@ -6,14 +6,14 @@ export const metadata = {
|
||||
|
||||
In this document, you’ll learn how to implement database operations, such as creating a record, in the main service.
|
||||
|
||||
## Use Data Model Services
|
||||
## Use the Data Model's Generated Service
|
||||
|
||||
The module container has a generated service registered for each data model. You can resolve that service and use it to perform database operations on the data model.
|
||||
To perform database operations on a data model, use the model's generated service in the module's container.
|
||||
|
||||
For example:
|
||||
|
||||
export const highlights = [
|
||||
["13", "", "Inject myCustomService, which is the service generated by the container loader for the MyCustom data model."],
|
||||
["13", "", "Inject myCustomService, which is the generated service of the `MyCustom` data model."],
|
||||
["22", "", "Add a new field for the generated service of the MyCustom data model."],
|
||||
["29", "", "Set the class field to the injected dependency."],
|
||||
["35", "create", "Use the `create` method of the generated service."]
|
||||
@@ -55,8 +55,7 @@ class HelloModuleService extends ModulesSdkUtils
|
||||
data: CreateMyCustomDTO
|
||||
): Promise<MyCustomDTO> {
|
||||
const myCustom = await this.myCustomService_.create(
|
||||
data,
|
||||
context
|
||||
data
|
||||
)
|
||||
|
||||
return myCustom
|
||||
|
||||
@@ -10,137 +10,14 @@ In this chapter, you’ll learn what a link module is and how to use the remote
|
||||
|
||||
A link module is a module whose only purpose is to define a relationship between two modules’ data models. The relationship is represented as a pivot or link table in the database, pointing at the primary keys of each data model.
|
||||
|
||||
For example, Medusa defines a link module between the Product and Pricing modules. The link module builds a relationship between the `ProductVariant` data model and the `PriceSet` data model.
|
||||
For example, Medusa has a link module that defines a relationship between the Product and Pricing modules. It links the `ProductVariant` and `PriceSet` data models.
|
||||
|
||||

|
||||
|
||||
Link modules provide more flexibility in managing relationships between modules while maintaining module isolation.
|
||||
Link modules provide more flexibility in managing relationships between modules while maintaining module isolation. The Medusa application only creates the link tables when both modules are available.
|
||||
|
||||
Link modules are currently only available for Medusa’s commerce modules. The Medusa application only creates the link tables when both modules are available.
|
||||
<Note type="soon">
|
||||
|
||||
---
|
||||
Link modules are currently only available for Medusa’s commerce modules.
|
||||
|
||||
## What is the Remote Link?
|
||||
|
||||
The remote link is a class with utility methods to manage links between modules. It’s registered in the Medusa container under the `remoteLink` registration name.
|
||||
|
||||
For example:
|
||||
|
||||
```ts
|
||||
import {
|
||||
MedusaRequest,
|
||||
MedusaResponse,
|
||||
} from "@medusajs/medusa"
|
||||
import {
|
||||
ModuleRegistrationName,
|
||||
RemoteLink,
|
||||
} from "@medusajs/modules-sdk"
|
||||
import {
|
||||
ContainerRegistrationKeys,
|
||||
} from "@medusajs/utils"
|
||||
|
||||
export async function POST(
|
||||
req: MedusaRequest,
|
||||
res: MedusaResponse
|
||||
): Promise<void> {
|
||||
const remoteLink: RemoteLink = req.scope.resolve(
|
||||
ContainerRegistrationKeys.REMOTE_LINK
|
||||
)
|
||||
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
You can use its methods to manage links, such as create or delete links.
|
||||
|
||||
### Create Link
|
||||
|
||||
To create a link between records of two data models, use the `create` method of the remote link.
|
||||
|
||||
For example:
|
||||
|
||||
```ts
|
||||
import { Modules } from "@medusajs/utils"
|
||||
|
||||
// ...
|
||||
|
||||
await remoteLink.create({
|
||||
[Modules.PRODUCT]: {
|
||||
variant_id: product.variants[0].id,
|
||||
},
|
||||
[Modules.PRICING]: {
|
||||
price_set_id: price.id,
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
The `create` method accepts as a parameter an object. The object’s keys are the names of the linked modules.
|
||||
|
||||
The value of each module’s property is an object. It defines the values of the linked fields.
|
||||
|
||||
So, in the example above, you specify for the Product Module the value of the `variant_id`, and for the Pricing Module the value of `price_set_id`. These are the fields linked between the models of the two modules.
|
||||
|
||||
### Dismiss Link
|
||||
|
||||
To remove a link between records of two data models, use the `dismiss` method of the remote link. This doesn’t remove the records, only the relation between them.
|
||||
|
||||
For example:
|
||||
|
||||
```ts
|
||||
import { Modules } from "@medusajs/utils"
|
||||
|
||||
// ...
|
||||
|
||||
await remoteLink.dismiss({
|
||||
[Modules.PRODUCT]: {
|
||||
variant_id: product.variants[0].id,
|
||||
},
|
||||
[Modules.PRICING]: {
|
||||
price_set_id: price.id,
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
The `dismiss` method accepts the same parameter type as the [create method](#create-link).
|
||||
|
||||
### Cascade Delete Linked Records
|
||||
|
||||
If a record, such as a variant, is deleted, use the `delete` method of the remote link to delete all associated links with cascade delete enabled.
|
||||
|
||||
For example:
|
||||
|
||||
```ts
|
||||
import { Modules } from "@medusajs/utils"
|
||||
|
||||
// ...
|
||||
|
||||
await productModuleService.deleteVariants([variant.id])
|
||||
|
||||
await remoteLink.delete({
|
||||
[Modules.PRODUCT]: {
|
||||
variant_id: variant.id,
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
This deletes all records linked to the deleted variant with cascade delete enabled in their relationship.
|
||||
|
||||
### Restore Linked Records
|
||||
|
||||
If a record, such as a variant, that was previously soft-deleted is now restored, use the `restore` method of the remote link to restore all associated links that were cascade deleted.
|
||||
|
||||
For example:
|
||||
|
||||
```ts
|
||||
import { Modules } from "@medusajs/utils"
|
||||
|
||||
// ...
|
||||
|
||||
await productModuleService.restoreVariants([variant.id])
|
||||
|
||||
await remoteLink.restore({
|
||||
[Modules.PRODUCT]: {
|
||||
variant_id: variant.id,
|
||||
},
|
||||
})
|
||||
```
|
||||
</Note>
|
||||
|
||||
@@ -40,18 +40,16 @@ The Medusa application resolves these relationships while maintaining isolation
|
||||
|
||||
Consider you’re creating a data model that adds custom fields associated with a product:
|
||||
|
||||
```ts title="src/modules/hello/models/custom-product-data.ts" highlights={[["19"]]}
|
||||
import { generateEntityId } from "@medusajs/utils"
|
||||
```ts title="src/modules/hello/models/custom-product-data.ts" highlights={[["17"]]}
|
||||
import { BaseEntity } from "@medusajs/utils"
|
||||
import {
|
||||
BeforeCreate,
|
||||
Entity,
|
||||
OnInit,
|
||||
PrimaryKey,
|
||||
Property,
|
||||
} from "@mikro-orm/core"
|
||||
|
||||
@Entity()
|
||||
export class CustomProductData {
|
||||
export class CustomProductData extends BaseEntity {
|
||||
@PrimaryKey({ columnType: "text" })
|
||||
id!: string
|
||||
|
||||
@@ -60,16 +58,6 @@ export class CustomProductData {
|
||||
|
||||
@Property({ columnType: "text", nullable: true })
|
||||
product_id?: string
|
||||
|
||||
@BeforeCreate()
|
||||
onCreate() {
|
||||
this.id = generateEntityId(this.id, "cpd")
|
||||
}
|
||||
|
||||
@OnInit()
|
||||
OnInit() {
|
||||
this.id = generateEntityId(this.id, "cpd")
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
@@ -81,7 +69,6 @@ When you add a new data model, make sure to:
|
||||
|
||||
- [Create a migration for it.](../../../basics/data-models/page.mdx#create-a-migration)
|
||||
- [Add it to the second parameter of the main service's factory function.](../service-factory/page.mdx#abstractModuleServiceFactory-parameters)
|
||||
- Add it to the [container](../container/page.mdx) and [connection](../connection-loader/page.mdx) loaders.
|
||||
|
||||
</Note>
|
||||
|
||||
@@ -115,7 +102,6 @@ class HelloModuleService extends ModulesSdkUtils
|
||||
__joinerConfig(): ModuleJoinerConfig {
|
||||
return {
|
||||
serviceName: "helloModuleService",
|
||||
primaryKeys: ["id"],
|
||||
alias: [
|
||||
{
|
||||
name: ["my_custom"],
|
||||
@@ -159,12 +145,6 @@ This creates a relationship to the `Product` data model of the Product Module us
|
||||
optional: false,
|
||||
description: "The name of your module (as added in `medusa-config.js`)."
|
||||
},
|
||||
{
|
||||
name: "primaryKeys",
|
||||
type: "`string[]`",
|
||||
optional: false,
|
||||
description: "The primary key field names used in your module's data models.",
|
||||
},
|
||||
{
|
||||
name: "alias",
|
||||
type: "`object[]`",
|
||||
@@ -240,8 +220,6 @@ const modules = {
|
||||
helloModuleService: {
|
||||
// ...
|
||||
definition: {
|
||||
key: "helloModuleService",
|
||||
registrationName: "helloModuleService",
|
||||
isQueryable: true,
|
||||
},
|
||||
},
|
||||
@@ -251,28 +229,6 @@ const modules = {
|
||||
|
||||
Enabling the `isQueryable` property is required to use relationships in a module.
|
||||
|
||||
The `definition` property’s value is an object that accepts the following properties, among others:
|
||||
|
||||
<TypeList types={[
|
||||
{
|
||||
name: "key",
|
||||
type: "`string`",
|
||||
optional: false,
|
||||
description: "The module's key in the `modules` object."
|
||||
},
|
||||
{
|
||||
name: "registrationName",
|
||||
type: "`string`",
|
||||
optional: false,
|
||||
description: "The name that the main service is registered under in the Medusa container. It’s recommended to be the same as `key`'s value.",
|
||||
},
|
||||
{
|
||||
name: "isQueryable",
|
||||
type: "`boolean`",
|
||||
description: "Whether the module is queryable. This must be enabled to allow a module to have relationships."
|
||||
}
|
||||
]} sectionTitle="Adjust Module Configuration" />
|
||||
|
||||
---
|
||||
|
||||
## Reference Inner Data Models
|
||||
|
||||
@@ -0,0 +1,198 @@
|
||||
export const metadata = {
|
||||
title: `${pageNumber} Remote Link`,
|
||||
}
|
||||
|
||||
# {metadata.title}
|
||||
|
||||
In this chapter, you’ll learn what the remote link is and how to use it to manage links.
|
||||
|
||||
## What is the Remote Link?
|
||||
|
||||
The remote link is a class with utility methods to manage links defined by the link module. It’s registered in the Medusa container under the `remoteLink` registration name.
|
||||
|
||||
For example:
|
||||
|
||||
```ts
|
||||
import {
|
||||
MedusaRequest,
|
||||
MedusaResponse,
|
||||
} from "@medusajs/medusa"
|
||||
import {
|
||||
ModuleRegistrationName,
|
||||
RemoteLink,
|
||||
} from "@medusajs/modules-sdk"
|
||||
import {
|
||||
ContainerRegistrationKeys,
|
||||
} from "@medusajs/utils"
|
||||
|
||||
export async function POST(
|
||||
req: MedusaRequest,
|
||||
res: MedusaResponse
|
||||
): Promise<void> {
|
||||
const remoteLink: RemoteLink = req.scope.resolve(
|
||||
ContainerRegistrationKeys.REMOTE_LINK
|
||||
)
|
||||
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
You can use its methods to manage links, such as create or delete links.
|
||||
|
||||
### Create Link
|
||||
|
||||
To create a link between records of two data models, use the `create` method of the remote link.
|
||||
|
||||
For example:
|
||||
|
||||
```ts
|
||||
import { Modules } from "@medusajs/utils"
|
||||
|
||||
// ...
|
||||
|
||||
await remoteLink.create({
|
||||
[Modules.PRODUCT]: {
|
||||
variant_id: product.variants[0].id,
|
||||
},
|
||||
[Modules.PRICING]: {
|
||||
price_set_id: price.id,
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
The `create` method accepts as a parameter an object. The object’s keys are the names of the linked modules.
|
||||
|
||||
The value of each module’s property is an object. It defines the values of the linked fields.
|
||||
|
||||
So, in the example above, you specify for the Product Module the value of the `variant_id`, and for the Pricing Module the value of `price_set_id`. These are the fields linked between the models of the two modules.
|
||||
|
||||
### Dismiss Link
|
||||
|
||||
To remove a link between records of two data models, use the `dismiss` method of the remote link. This doesn’t remove the records, only the relation between them.
|
||||
|
||||
For example:
|
||||
|
||||
```ts
|
||||
import { Modules } from "@medusajs/utils"
|
||||
|
||||
// ...
|
||||
|
||||
await remoteLink.dismiss({
|
||||
[Modules.PRODUCT]: {
|
||||
variant_id: product.variants[0].id,
|
||||
},
|
||||
[Modules.PRICING]: {
|
||||
price_set_id: price.id,
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
The `dismiss` method accepts the same parameter type as the [create method](#create-link).
|
||||
|
||||
### Cascade Delete Linked Records
|
||||
|
||||
If a record, such as a variant, is deleted, use the `delete` method of the remote link to delete all associated links with cascade delete enabled.
|
||||
|
||||
For example:
|
||||
|
||||
```ts
|
||||
import { Modules } from "@medusajs/utils"
|
||||
|
||||
// ...
|
||||
|
||||
await productModuleService.deleteVariants([variant.id])
|
||||
|
||||
await remoteLink.delete({
|
||||
[Modules.PRODUCT]: {
|
||||
variant_id: variant.id,
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
This deletes all records linked to the deleted variant with cascade delete enabled in their relationship.
|
||||
|
||||
### Restore Linked Records
|
||||
|
||||
If a record, such as a variant, that was previously soft-deleted is now restored, use the `restore` method of the remote link to restore all associated links that were cascade deleted.
|
||||
|
||||
For example:
|
||||
|
||||
```ts
|
||||
import { Modules } from "@medusajs/utils"
|
||||
|
||||
// ...
|
||||
|
||||
await productModuleService.restoreVariants([variant.id])
|
||||
|
||||
await remoteLink.restore({
|
||||
[Modules.PRODUCT]: {
|
||||
variant_id: variant.id,
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Link Module's Service
|
||||
|
||||
The remote link has a `getLinkModule` method to retrieve the service of the link module. This service has `list` and `retrieve` methods to retrieve the linked items.
|
||||
|
||||
For example, to retrieve the link module of the Product and Pricing modules:
|
||||
|
||||
export const linkModuleServiceHighlights = [
|
||||
["6", "Modules.PRODUCT", "The name of the first module in the link module's definition."],
|
||||
["7", '"variant_id"', "The foreign key that links to the record in the first module."],
|
||||
["8", "Modules.PRICING", "The name of the second module in the link module's definition."],
|
||||
["9", '"price_set_id"', "The foreign key that links to the record in the second module."],
|
||||
["12", "", "The link module's service is undefined if either of the modules isn't installed or there's no link module with the specified definition."]
|
||||
]
|
||||
|
||||
```ts highlights={linkModuleServiceHighlights}
|
||||
import { Modules } from "@medusajs/utils"
|
||||
|
||||
// ...
|
||||
|
||||
const linkModuleService = remoteLink.getLinkModule(
|
||||
Modules.PRODUCT,
|
||||
"variant_id",
|
||||
Modules.PRICING,
|
||||
"price_set_id"
|
||||
)
|
||||
|
||||
if (!linkModuleService) {
|
||||
return
|
||||
}
|
||||
```
|
||||
|
||||
The `getLinkModule` method accepts four parameter:
|
||||
|
||||
1. A string indicating the name of the first module in the link module's definition.
|
||||
2. A string indicating the foreign key that links to the record in the first module.
|
||||
3. A string indicating the name of the second module in the link module's definition.
|
||||
4. A string indicating the foreign key that links to the record in the second module.
|
||||
|
||||
Notice that the returned link module service might be undefined if either of the modules isn't installed, or if there's no link module with the specified definition.
|
||||
|
||||
### List Linked Items
|
||||
|
||||
The link module's service has a `list` method that retrieves a list of linked records. It also accepts filters to retrieve specific linked items.
|
||||
|
||||
For example, to retrieve the price sets linked to a variant:
|
||||
|
||||
```ts
|
||||
import { Modules } from "@medusajs/utils"
|
||||
|
||||
// ...
|
||||
|
||||
const linkModuleService = remoteLink.getLinkModule(
|
||||
Modules.PRODUCT,
|
||||
"variant_id",
|
||||
Modules.PRICING,
|
||||
"price_set_id"
|
||||
)
|
||||
|
||||
const items = await linkModuleService.list(
|
||||
{ variant_id: [variant.id] },
|
||||
{ select: ["variant_id", "price_set_id"] }
|
||||
)
|
||||
```
|
||||
@@ -114,7 +114,7 @@ The `remoteQueryObjectFromString` function accepts a `variables` property. You c
|
||||
|
||||
## Sort Records
|
||||
|
||||
```ts highlights={[["4"], ["5"], ["6"]]}
|
||||
```ts highlights={[["5"], ["6"], ["7"]]}
|
||||
const query = remoteQueryObjectFromString({
|
||||
entryPoint: "custom_product_data",
|
||||
fields: ["id", "custom_field", "product.title"],
|
||||
|
||||
@@ -22,15 +22,6 @@ Medusa provides a service factory that your module’s main service can extend.
|
||||
|
||||
## How to Extend the Service Factory?
|
||||
|
||||
<Note type="check">
|
||||
|
||||
Your module must be using the following loaders from the previous chapters:
|
||||
|
||||
- [Container Loader](../container/page.mdx).
|
||||
- [Connection Loader](../connection-loader/page.mdx).
|
||||
|
||||
</Note>
|
||||
|
||||
Medusa provides the service factory as a function your service extends. The function creates and returns a service class with generated data-management methods.
|
||||
|
||||
For example, create the file `src/modules/hello/service.ts` with the following content:
|
||||
|
||||
@@ -9,9 +9,9 @@ In the previous chapters, you got a brief introduction to Medusa’s basic conce
|
||||
The next chapters dive deeper into each concept. By the end of these chapters, you’ll be able to:
|
||||
|
||||
- Expose API routes with control over authentication, parsing request bodies, and more.
|
||||
- Create data models with complex fields and relations.
|
||||
- Manage data models in services.
|
||||
- Create relationships between modules.
|
||||
- Create data models with complex fields and relations.
|
||||
- Create loaders outside of modules.
|
||||
- Access events payloads.
|
||||
- Create advanced workflows and configure retries and timeout.
|
||||
|
||||
Reference in New Issue
Block a user