docs: add guide on how to seed data (#9449)
Add a guide on how to seed data using a custom CLI script, with an example to seed products Closes DOCS-953
This commit is contained in:
@@ -0,0 +1,200 @@
|
||||
export const metadata = {
|
||||
title: `${pageNumber} Seed Data with Custom CLI Script`,
|
||||
}
|
||||
|
||||
# {metadata.title}
|
||||
|
||||
In this chapter, you'll learn how to seed data using a custom CLI script.
|
||||
|
||||
## How to Seed Data
|
||||
|
||||
To seed dummy data for development or demo purposes, use a custom CLI script.
|
||||
|
||||
In the CLI script, use your custom workflows or Medusa's existing workflows, which you can browse in [this reference](!resources!/medusa-workflows-reference), to seed data.
|
||||
|
||||
### Example: Seed Dummy Products
|
||||
|
||||
In this section, you'll follow an example of creating a custom CLI script that seeds fifty dummy products.
|
||||
|
||||
First, install the [Faker](https://fakerjs.dev/) library to generate random data in your script:
|
||||
|
||||
```bash npm2yarn
|
||||
npm install --save-dev @faker-js/faker
|
||||
```
|
||||
|
||||
Then, create the file `src/scripts/demo-products.ts` with the following content:
|
||||
|
||||
export const highlights = [
|
||||
["16", "salesChannelModuleService", "Resolve the Sales Chanel Module's main service"],
|
||||
["19", "logger", "Resolve the logger to log messages in the terminal."],
|
||||
["22", "query", "Resolve Query to retrieve data later."],
|
||||
["26", "defaultSalesChannel", "Retrieve the default sales channel to associate products with."],
|
||||
["31", "sizeOptions", "Declare the size options to be used in the products' variants."],
|
||||
["32", "colorOptions", "Declare the color options to be used in the products' variants."],
|
||||
["33", "currency_code", "Declare the currency code to use in products' prices."],
|
||||
["34", "productsNum", "The number of products to seed."]
|
||||
]
|
||||
|
||||
```ts title="src/scripts/demo-products.ts" highlights={highlights} collapsibleLines="1-12" expandButtonLabel="Show Imports"
|
||||
import { ExecArgs } from "@medusajs/framework/types"
|
||||
import { faker } from "@faker-js/faker"
|
||||
import {
|
||||
ContainerRegistrationKeys,
|
||||
Modules,
|
||||
ProductStatus,
|
||||
} from "@medusajs/framework/utils"
|
||||
import {
|
||||
createInventoryLevelsWorkflow,
|
||||
createProductsWorkflow,
|
||||
} from "@medusajs/medusa/core-flows"
|
||||
|
||||
export default async function seedDummyProducts({
|
||||
container,
|
||||
}: ExecArgs) {
|
||||
const salesChannelModuleService = container.resolve(
|
||||
Modules.SALES_CHANNEL
|
||||
)
|
||||
const logger = container.resolve(
|
||||
ContainerRegistrationKeys.LOGGER
|
||||
)
|
||||
const query = container.resolve(
|
||||
ContainerRegistrationKeys.QUERY
|
||||
)
|
||||
|
||||
const defaultSalesChannel = await salesChannelModuleService
|
||||
.listSalesChannels({
|
||||
name: "Default Sales Channel",
|
||||
})
|
||||
|
||||
const sizeOptions = ["S", "M", "L", "XL"]
|
||||
const colorOptions = ["Black", "White"]
|
||||
const currency_code = "eur"
|
||||
const productsNum = 50
|
||||
|
||||
// TODO seed products
|
||||
}
|
||||
```
|
||||
|
||||
So far, in the script, you:
|
||||
|
||||
- Resolve the Sales Channel Module's main service to retrieve the application's default sales channel. This is the sales channel the dummy products will be available in.
|
||||
- Resolve the Logger to log messages in the terminal, and Query to later retrieve data useful for the seeded products.
|
||||
- Initialize some default data to use when seeding the products next.
|
||||
|
||||
Next, replace the `TODO` with the following:
|
||||
|
||||
```ts title="src/scripts/demo-products.ts"
|
||||
const productsData = new Array(productsNum).fill(0).map((_, index) => {
|
||||
const title = faker.commerce.product() + "_" + index
|
||||
return {
|
||||
title,
|
||||
is_giftcard: true,
|
||||
description: faker.commerce.productDescription(),
|
||||
status: ProductStatus.PUBLISHED,
|
||||
options: [
|
||||
{
|
||||
title: "Size",
|
||||
values: sizeOptions,
|
||||
},
|
||||
{
|
||||
title: "Color",
|
||||
values: colorOptions,
|
||||
},
|
||||
],
|
||||
images: [
|
||||
{
|
||||
url: faker.image.urlPlaceholder({
|
||||
text: title,
|
||||
}),
|
||||
},
|
||||
{
|
||||
url: faker.image.urlPlaceholder({
|
||||
text: title,
|
||||
}),
|
||||
},
|
||||
],
|
||||
variants: new Array(10).fill(0).map((_, variantIndex) => ({
|
||||
title: `${title} ${variantIndex}`,
|
||||
sku: `variant-${variantIndex}${index}`,
|
||||
prices: new Array(10).fill(0).map((_, priceIndex) => ({
|
||||
currency_code,
|
||||
amount: 10 * priceIndex,
|
||||
})),
|
||||
options: {
|
||||
Size: sizeOptions[Math.floor(Math.random() * 3)],
|
||||
},
|
||||
})),
|
||||
sales_channels: [
|
||||
{
|
||||
id: defaultSalesChannel[0].id,
|
||||
},
|
||||
],
|
||||
}
|
||||
})
|
||||
|
||||
// TODO seed products
|
||||
```
|
||||
|
||||
You generate fifty products using the sales channel and variables you initialized, and using Faker for random data, such as the product's title or images.
|
||||
|
||||
Then, replace the new `TODO` with the following:
|
||||
|
||||
```ts title="src/scripts/demo-products.ts"
|
||||
const { result: products } = await createProductsWorkflow(container).run({
|
||||
input: {
|
||||
products: productsData,
|
||||
},
|
||||
})
|
||||
|
||||
logger.info(`Seeded ${products.length} products.`)
|
||||
|
||||
// TODO add inventory levels
|
||||
```
|
||||
|
||||
You create the generated products using the `createProductsWorkflow` imported previously from `@medusajs/medusa/core-flows`. It accepts the product data as input, and returns the created products.
|
||||
|
||||
Only thing left is to create inventory levels for the products. So, replace the last `TODO` with the following:
|
||||
|
||||
```ts title="src/scripts/demo-products.ts"
|
||||
logger.info("Seeding inventory levels.")
|
||||
|
||||
const { data: stockLocations } = await query.graph({
|
||||
entity: "stock_location",
|
||||
fields: ["id"],
|
||||
})
|
||||
|
||||
const { data: inventoryItems } = await query.graph({
|
||||
entity: "inventory_item",
|
||||
fields: ["id"],
|
||||
})
|
||||
|
||||
const inventoryLevels = inventoryItems.map((inventoryItem) => ({
|
||||
location_id: stockLocations[0].id,
|
||||
stocked_quantity: 1000000,
|
||||
inventory_item_id: inventoryItem.id,
|
||||
}))
|
||||
|
||||
await createInventoryLevelsWorkflow(container).run({
|
||||
input: {
|
||||
inventory_levels: inventoryLevels,
|
||||
},
|
||||
})
|
||||
|
||||
logger.info("Finished seeding inventory levels data.")
|
||||
```
|
||||
|
||||
You use Query to retrieve the stock location, to use the first location in the application, and the inventory items.
|
||||
|
||||
Then, you generate inventory levels for each inventory item, associating it with the first stock location.
|
||||
|
||||
Finally, you use the `createInventoryLevelsWorkflow` imported from `@medusajs/medusa/core-flows` to create the inventory levels.
|
||||
|
||||
### Test Script
|
||||
|
||||
To test out the script, run the following command in your project's directory:
|
||||
|
||||
```bash
|
||||
npx medusa exec ./src/scripts/demo-products.ts
|
||||
```
|
||||
|
||||
This seeds the products to your database. If you run your Medusa application and view the products in the dashboard, you'll find fifty new products.
|
||||
@@ -31,9 +31,9 @@ export default defineLink(
|
||||
extraColumns: {
|
||||
metadata: {
|
||||
type: "json",
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
)
|
||||
```
|
||||
@@ -94,16 +94,16 @@ Learn more about the remote link, how to resolve it, and its methods in [this ch
|
||||
```ts
|
||||
await remoteLink.create({
|
||||
[Modules.PRODUCT]: {
|
||||
product_id: "123"
|
||||
product_id: "123",
|
||||
},
|
||||
HELLO_MODULE: {
|
||||
my_custom_id: "321"
|
||||
my_custom_id: "321",
|
||||
},
|
||||
data: {
|
||||
metadata: {
|
||||
test: true
|
||||
}
|
||||
}
|
||||
test: true,
|
||||
},
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
@@ -138,8 +138,8 @@ const { data } = await query.graph({
|
||||
entity: productHelloLink.entryPoint,
|
||||
fields: ["metadata", "product.*", "my_custom.*"],
|
||||
filters: {
|
||||
product_id: "prod_123"
|
||||
}
|
||||
product_id: "prod_123",
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
@@ -160,15 +160,15 @@ For example:
|
||||
```ts
|
||||
await remoteLink.create({
|
||||
[Modules.PRODUCT]: {
|
||||
product_id: "123"
|
||||
product_id: "123",
|
||||
},
|
||||
HELLO_MODULE: {
|
||||
my_custom_id: "321"
|
||||
my_custom_id: "321",
|
||||
},
|
||||
data: {
|
||||
metadata: {
|
||||
test: false
|
||||
}
|
||||
}
|
||||
test: false,
|
||||
},
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
@@ -36,7 +36,7 @@ export const methodsHighlight = [
|
||||
// other imports...
|
||||
import {
|
||||
InjectManager,
|
||||
MedusaContext
|
||||
MedusaContext,
|
||||
} from "@medusajs/framework/utils"
|
||||
|
||||
class HelloModuleService {
|
||||
@@ -99,7 +99,7 @@ export const opHighlights = [
|
||||
import {
|
||||
InjectManager,
|
||||
InjectTransactionManager,
|
||||
MedusaContext
|
||||
MedusaContext,
|
||||
} from "@medusajs/framework/utils"
|
||||
import { Context } from "@medusajs/framework/types"
|
||||
import { EntityManager } from "@mikro-orm/knex"
|
||||
@@ -118,10 +118,10 @@ class HelloModuleService {
|
||||
await transactionManager.nativeUpdate(
|
||||
"my_custom",
|
||||
{
|
||||
id: input.id
|
||||
id: input.id,
|
||||
},
|
||||
{
|
||||
name: input.name
|
||||
name: input.name,
|
||||
}
|
||||
)
|
||||
|
||||
@@ -307,7 +307,7 @@ export const repoHighlights = [
|
||||
import {
|
||||
InjectManager,
|
||||
InjectTransactionManager,
|
||||
MedusaContext
|
||||
MedusaContext,
|
||||
} from "@medusajs/framework/utils"
|
||||
import { Context } from "@medusajs/framework/types"
|
||||
import { EntityManager } from "@mikro-orm/knex"
|
||||
@@ -327,10 +327,10 @@ class HelloModuleService {
|
||||
await transactionManager.nativeUpdate(
|
||||
"my_custom",
|
||||
{
|
||||
id: input.id
|
||||
id: input.id,
|
||||
},
|
||||
{
|
||||
name: input.name
|
||||
name: input.name,
|
||||
}
|
||||
)
|
||||
|
||||
@@ -342,7 +342,7 @@ class HelloModuleService {
|
||||
return updatedRecord
|
||||
},
|
||||
{
|
||||
transaction: sharedContext.transactionManager
|
||||
transaction: sharedContext.transactionManager,
|
||||
}
|
||||
)
|
||||
}
|
||||
@@ -397,7 +397,7 @@ class HelloModuleService {
|
||||
// ...
|
||||
},
|
||||
{
|
||||
transaction: sharedContext.transactionManager
|
||||
transaction: sharedContext.transactionManager,
|
||||
}
|
||||
)
|
||||
}
|
||||
@@ -430,7 +430,7 @@ class HelloModuleService {
|
||||
// ...
|
||||
},
|
||||
{
|
||||
isolationLevel: IsolationLevel.READ_COMMITTED
|
||||
isolationLevel: IsolationLevel.READ_COMMITTED,
|
||||
}
|
||||
)
|
||||
}
|
||||
@@ -456,7 +456,7 @@ class HelloModuleService {
|
||||
// ...
|
||||
},
|
||||
{
|
||||
enableNestedTransactions: false
|
||||
enableNestedTransactions: false,
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@@ -66,6 +66,12 @@ This chapter provides links to example sections on different Medusa topics.
|
||||
|
||||
---
|
||||
|
||||
## Custom CLI Scripts
|
||||
|
||||
- [Seed Dummy Products](../../advanced-development/custom-cli-scripts/seed-data/page.mdx)
|
||||
|
||||
---
|
||||
|
||||
## Admin Customizations
|
||||
|
||||
- [Send a request to custom API routes from widgets or UI routes](../../customization/customize-admin/widget/page.mdx)
|
||||
|
||||
@@ -108,9 +108,10 @@ export const generatedEditDates = {
|
||||
"app/customization/next-steps/page.mdx": "2024-09-12T10:50:04.873Z",
|
||||
"app/customization/page.mdx": "2024-09-12T11:16:18.504Z",
|
||||
"app/more-resources/cheatsheet/page.mdx": "2024-07-11T16:11:26.480Z",
|
||||
"app/more-resources/examples/page.mdx": "2024-09-27T07:17:16.892Z",
|
||||
"app/more-resources/examples/page.mdx": "2024-10-03T11:12:50.956Z",
|
||||
"app/architecture/architectural-modules/page.mdx": "2024-09-23T12:51:04.520Z",
|
||||
"app/architecture/overview/page.mdx": "2024-09-23T12:55:01.339Z",
|
||||
"app/advanced-development/data-models/infer-type/page.mdx": "2024-09-30T08:43:53.123Z",
|
||||
"app/advanced-development/custom-cli-scripts/seed-data/page.mdx": "2024-10-03T11:11:07.181Z",
|
||||
"app/basics/modules/page.mdx": "2024-10-03T13:05:49.551Z"
|
||||
}
|
||||
@@ -472,6 +472,13 @@ export const sidebar = numberSidebarItems(
|
||||
type: "link",
|
||||
path: "/advanced-development/custom-cli-scripts",
|
||||
title: "Custom CLI Scripts",
|
||||
children: [
|
||||
{
|
||||
type: "link",
|
||||
path: "/advanced-development/custom-cli-scripts/seed-data",
|
||||
title: "Seed Data",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: "link",
|
||||
|
||||
Reference in New Issue
Block a user