* initialized next.js project * finished markdown sections * added operation schema component * change page metadata * eslint fixes * fixes related to deployment * added response schema * resolve max stack issue * support for different property types * added support for property types * added loading for components * added more loading * type fixes * added oneOf type * removed console * fix replace with push * refactored everything * use static content for description * fixes and improvements * added code examples section * fix path name * optimizations * fixed tag navigation * add support for admin and store references * general enhancements * optimizations and fixes * fixes and enhancements * added search bar * loading enhancements * added loading * added code blocks * added margin top * add empty response text * fixed oneOf parameters * added path and query parameters * general fixes * added base path env variable * small fix for arrays * enhancements * design enhancements * general enhancements * fix isRequired * added enum values * enhancements * general fixes * general fixes * changed oas generation script * additions to the introduction section * added copy button for code + other enhancements * fix response code block * fix metadata * formatted store introduction * move sidebar logic to Tags component * added test env variables * fix code block bug * added loading animation * added expand param + loading * enhance operation loading * made responsive + improvements * added loading provider * fixed loading * adjustments for small devices * added sidebar label for endpoints * added feedback component * fixed analytics * general fixes * listen to scroll for other headings * added sample env file * update api ref files + support new fields * fix for external docs link * added new sections * fix last item in sidebar not showing * move docs content to www/docs * change redirect url * revert change * resolve build errors * configure rewrites * changed to environment variable url * revert changing environment variable name * add environment variable for API path * fix links * fix tailwind settings * remove vercel file * reconfigured api route * move api page under api * fix page metadata * fix external link in navigation bar * update api spec * updated api specs * fixed google lint error * add max-height on request samples * add padding before loading * fix for one of name * fix undefined types * general fixes * remove response schema example * redesigned navigation bar * redesigned sidebar * fixed up paddings * added feedback component + report issue * fixed up typography, padding, and general styling * redesigned code blocks * optimization * added error timeout * fixes * added indexing with algolia + fixes * fix errors with algolia script * redesign operation sections * fix heading scroll * design fixes * fix padding * fix padding + scroll issues * fix scroll issues * improve scroll performance * fixes for safari * optimization and fixes * fixes to docs + details animation * padding fixes for code block * added tab animation * fixed incorrect link * added selection styling * fix lint errors * redesigned details component * added detailed feedback form * api reference fixes * fix tabs * upgrade + fixes * updated documentation links * optimizations to sidebar items * fix spacing in sidebar item * optimizations and fixes * fix endpoint path styling * remove margin * final fixes * change margin on small devices * generated OAS * fixes for mobile * added feedback modal * optimize dark mode button * fixed color mode useeffect * minimize dom size * use new style system * radius and spacing design system * design fixes * fix eslint errors * added meta files * change cron schedule * fix docusaurus configurations * added operating system to feedback data * change content directory name * fixes to contribution guidelines * revert renaming content * added api-reference to documentation workflow * fixes for search * added dark mode + fixes * oas fixes * handle bugs * added code examples for clients * changed tooltip text * change authentication to card * change page title based on selected section * redesigned mobile navbar * fix icon colors * fix key colors * fix medusa-js installation command * change external regex in algolia * change changeset * fix padding on mobile * fix hydration error * update depedencies
450 lines
13 KiB
Plaintext
450 lines
13 KiB
Plaintext
---
|
||
description: 'Learn how to create a batch job strategy in Medusa. This guide also includes how to test your batch job strategy.'
|
||
addHowToData: true
|
||
---
|
||
|
||
import Tabs from '@theme/Tabs';
|
||
import TabItem from '@theme/TabItem';
|
||
|
||
# Create a Batch Job Strategy
|
||
|
||
In this document, you’ll learn how to create a batch job strategy in Medusa.
|
||
|
||
:::info
|
||
|
||
If you’re interested to learn more about what Batch Jobs are and how they work, check out [this documentation](./index.mdx).
|
||
|
||
:::
|
||
|
||
## Overview
|
||
|
||
Batch jobs can be used to perform long tasks in the background of your Medusa backend. Batch jobs are handled by batch job strategies. An example of a batch job strategy is the Import Products functionality.
|
||
|
||
This documentation helps you learn how to create a batch job strategy. The batch job strategy used in this example changes the status of all draft products to `published`.
|
||
|
||
---
|
||
|
||
## Prerequisites
|
||
|
||
### Medusa Components
|
||
|
||
It is assumed that you already have a Medusa backend installed and set up. If not, you can follow our [quickstart guide](../backend/install.mdx) to get started. The Medusa backend must also have an event bus module installed, which is available when using the default Medusa backend starter.
|
||
|
||
---
|
||
|
||
## 1. Create a File
|
||
|
||
A batch job strategy is essentially a class defined in a TypeScript or JavaScript file. You should create this file in `src/strategies`.
|
||
|
||
Following the example used in this documentation, create the file `src/strategies/publish.ts`.
|
||
|
||
---
|
||
|
||
## 2. Create Class
|
||
|
||
Batch job strategies must extend the abstract class `AbstractBatchJobStrategy` and implement its abstract methods.
|
||
|
||
Add the following content to the file you created:
|
||
|
||
```ts title=src/strategies/publish.ts
|
||
import {
|
||
AbstractBatchJobStrategy,
|
||
BatchJobService,
|
||
} from "@medusajs/medusa"
|
||
import { EntityManager } from "typeorm"
|
||
|
||
class PublishStrategy extends AbstractBatchJobStrategy {
|
||
protected batchJobService_: BatchJobService
|
||
processJob(batchJobId: string): Promise<void> {
|
||
throw new Error("Method not implemented.")
|
||
}
|
||
buildTemplate(): Promise<string> {
|
||
throw new Error("Method not implemented.")
|
||
}
|
||
protected manager_: EntityManager
|
||
protected transactionManager_: EntityManager
|
||
|
||
}
|
||
|
||
export default PublishStrategy
|
||
```
|
||
|
||
---
|
||
|
||
## 3. Define Required Properties
|
||
|
||
A batch job strategy class must have two static properties: the `identifier` and `batchType` properties. The `identifier` must be a unique string associated with your batch job strategy, and `batchType` must be the batch job's type.
|
||
|
||
You will use the `batchType` later when you [interact with the Batch Job APIs](#test-your-batch-job-strategy).
|
||
|
||
Following the same example, add the following properties to the `PublishStrategy` class:
|
||
|
||
```ts
|
||
class PublishStrategy extends AbstractBatchJobStrategy {
|
||
static identifier = "publish-products-strategy"
|
||
static batchType = "publish-products"
|
||
|
||
// ...
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 4. Define Methods
|
||
|
||
### (Optional) prepareBatchJobForProcessing
|
||
|
||
Medusa runs this method before it creates the batch job to prepare the content of the batch job record in the database. It accepts two parameters: the batch job data sent in the body of the [Create Batch Job request](https://docs.medusajs.com/api/admin#batch-jobs_postbatchjobs), and the request instance.
|
||
|
||
Implementing this method is optional. For example:
|
||
|
||
```ts
|
||
class PublishStrategy extends AbstractBatchJobStrategy {
|
||
// ...
|
||
async prepareBatchJobForProcessing(
|
||
batchJob: CreateBatchJobInput,
|
||
req: Express.Request
|
||
): Promise<CreateBatchJobInput> {
|
||
// make changes to the batch job's fields...
|
||
return batchJob
|
||
}
|
||
}
|
||
```
|
||
|
||
### (Optional) preProcessBatchJob
|
||
|
||
Medusa runs this method after it creates the batch job, but before it is confirmed and processed. You can use this method to perform any necessary action before the batch job is processed. You can also use this method to add information related to the expected result.
|
||
|
||
For example, this implementation of the `preProcessBatchJob` method calculates how many draft products it will published and adds it to the `result` attribute of the batch job:
|
||
|
||
```ts
|
||
class PublishStrategy extends AbstractBatchJobStrategy {
|
||
// ...
|
||
async preProcessBatchJob(batchJobId: string): Promise<void> {
|
||
return await this.atomicPhase_(
|
||
async (transactionManager) => {
|
||
const batchJob = (await this.batchJobService_
|
||
.withTransaction(transactionManager)
|
||
.retrieve(batchJobId))
|
||
|
||
const count = await this.productService_
|
||
.withTransaction(transactionManager)
|
||
.count({
|
||
status: ProductStatus.DRAFT,
|
||
})
|
||
|
||
await this.batchJobService_
|
||
.withTransaction(transactionManager)
|
||
.update(batchJob, {
|
||
result: {
|
||
advancement_count: 0,
|
||
count,
|
||
stat_descriptors: [
|
||
{
|
||
key: "product-publish-count",
|
||
name: "Number of products to publish",
|
||
message:
|
||
`${count} product(s) will be published.`,
|
||
},
|
||
],
|
||
},
|
||
})
|
||
})
|
||
}
|
||
}
|
||
```
|
||
|
||
The `result` attribute is an object that can hold many properties including:
|
||
|
||
- `count`: used to indicate how many items (in this case, products) that the task will run on.
|
||
- `advancement_count`: used to indicate the current number of items processed at a given moment. Since the batch job isn't processed yet, you set it to zero.
|
||
- `stat_descriptors`: can be used to show human-readable messages.
|
||
|
||
### processJob
|
||
|
||
Medusa runs this method to process the batch job once it is confirmed.
|
||
|
||
For example, this implementation of the `processJob` method retrieves all draft products and changes their status to published:
|
||
|
||
```ts
|
||
class PublishStrategy extends AbstractBatchJobStrategy {
|
||
// ...
|
||
async processJob(batchJobId: string): Promise<void> {
|
||
return await this.atomicPhase_(
|
||
async (transactionManager) => {
|
||
const productServiceTx = this.productService_
|
||
.withTransaction(transactionManager)
|
||
|
||
const productList = await productServiceTx
|
||
.list({
|
||
status: [ProductStatus.DRAFT],
|
||
})
|
||
|
||
productList.forEach(async (product: Product) => {
|
||
await productServiceTx
|
||
.update(product.id, {
|
||
status: ProductStatus.PUBLISHED,
|
||
})
|
||
})
|
||
|
||
await this.batchJobService_
|
||
.withTransaction(transactionManager)
|
||
.update(batchJobId, {
|
||
result: {
|
||
advancement_count: productList.length,
|
||
},
|
||
})
|
||
}
|
||
)
|
||
}
|
||
}
|
||
```
|
||
|
||
:::note
|
||
|
||
When a batch job is canceled, the processing of the batch job doesn’t automatically stop. You will have to manually check for changes in the status of the batch job. For example, you can retrieve the batch job and use the condition `batchJob.status === BatchJobStatus.CANCELED` to check if the batch job was canceled.
|
||
|
||
:::
|
||
|
||
### buildTemplate
|
||
|
||
This method can be used in cases where you provide a template file to download, such as when implementing an import or export functionality.
|
||
|
||
If not necessary to your use case, you can simply return an empty string:
|
||
|
||
```ts
|
||
class PublishStrategy extends AbstractBatchJobStrategy {
|
||
// ...
|
||
async buildTemplate(): Promise<string> {
|
||
return ""
|
||
}
|
||
}
|
||
```
|
||
|
||
### (Optional) shouldRetryOnProcessingError
|
||
|
||
Medusa uses this method to decide whether it should retry the batch job if an error occurs during processing.
|
||
|
||
By default, the `AbstractBatchJobStrategy` class implements this method and returns `false`, indicating that if a batch job’s process fails it will not be retried.
|
||
|
||
If you would like to change that behavior, you can override this method to return a different value:
|
||
|
||
```ts
|
||
class PublishStrategy extends AbstractBatchJobStrategy {
|
||
// ...
|
||
protected async shouldRetryOnProcessingError(
|
||
batchJob: BatchJob,
|
||
err: unknown
|
||
): Promise<boolean> {
|
||
return true
|
||
}
|
||
}
|
||
```
|
||
|
||
### (Optional) handleProcessingError
|
||
|
||
Medusa uses this method to handle errors that occur during processing. By default, it changes the status of the batch job to `failed` and sets the `errors` property of the batch job’s `result` attribute.
|
||
|
||
You can use this method as implemented in `AbstractBatchJobStrategy` at any point in your batch job process to set the batch job as failed.
|
||
|
||
You can also override this method in your batch job strategy and change how it works:
|
||
|
||
```ts
|
||
class PublishStrategy extends AbstractBatchJobStrategy {
|
||
// ...
|
||
protected async handleProcessingError<T>(
|
||
batchJobId: string,
|
||
err: unknown,
|
||
result: T
|
||
): Promise<void> {
|
||
// different implementation...
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 5. Run Build Command
|
||
|
||
After you create the batch job and before testing it out, you must run the build command in the directory of your Medusa backend:
|
||
|
||
```bash npm2yarn
|
||
npm run build
|
||
```
|
||
|
||
---
|
||
|
||
## Test your Batch Job Strategy
|
||
|
||
This section covers how to test and use your batch job strategy. Make sure to start your backend first:
|
||
|
||
```bash
|
||
npx medusa develop
|
||
```
|
||
|
||
You must also use an authenticated user to send batch job requests. You can refer to the [authentication guide in the API reference](https://docs.medusajs.com/api/admin#authentication) for more details.
|
||
|
||
If you follow along with the JS Client code snippets, make sure to [install and set it up first](../../js-client/overview.md).
|
||
|
||
### Create Batch Job
|
||
|
||
The first step is to create a batch job using the [Create Batch Job endpoint](https://docs.medusajs.com/api/admin#batch-jobs_postbatchjobs). In the body of the request, you must set the `type` to the value of `batchType` in the batch job strategy you created.
|
||
|
||
For example, this creates a batch job of the type `publish-products`:
|
||
|
||
<Tabs groupId="request-types" isCodeTabs={true}>
|
||
<TabItem value="client" label="Medusa JS Client" default>
|
||
|
||
```jsx
|
||
medusa.admin.batchJobs.create({
|
||
type: "publish-products",
|
||
context: { },
|
||
dry_run: true,
|
||
})
|
||
.then(( batch_job ) => {
|
||
console.log(batch_job.status)
|
||
})
|
||
```
|
||
|
||
</TabItem>
|
||
<TabItem value="fetch" label="Fetch API">
|
||
|
||
```jsx
|
||
fetch(`<BACKEND_URL>/admin/batch-jobs`, {
|
||
method: "POST",
|
||
credentials: "include",
|
||
headers: {
|
||
"Content-Type": "application/json",
|
||
},
|
||
body: JSON.stringify({
|
||
type: "publish-products",
|
||
context: { },
|
||
dry_run: true,
|
||
}),
|
||
})
|
||
.then((response) => response.json())
|
||
.then(({ batch_job }) => {
|
||
console.log(batch_job.status)
|
||
})
|
||
```
|
||
|
||
</TabItem>
|
||
<TabItem value="curl" label="cURL">
|
||
|
||
```bash
|
||
curl -L -X POST '<BACKEND_URL>/admin/batch-jobs' \
|
||
-H 'Authorization: Bearer <API_TOKEN>' \
|
||
-H 'Content-Type: application/json' \
|
||
--data-raw '{
|
||
"type": "publish-products",
|
||
"context": { },
|
||
"dry_run": true
|
||
}'
|
||
```
|
||
|
||
</TabItem>
|
||
</Tabs>
|
||
|
||
You set the `dry_run` to `true` to disable automatic confirmation and running of the batch job. If you want the batch job to run automatically, you can remove this body parameter.
|
||
|
||
Make sure to replace `<BACKEND_URL>` with the backend URL where applicable.
|
||
|
||
### (Optional) Retrieve Batch Job
|
||
|
||
You can retrieve the batch job afterward to get its status and view details about the process in the `result` property:
|
||
|
||
<Tabs groupId="request-type" isCodeTabs={true}>
|
||
<TabItem value="client" label="Medusa JS Client" default>
|
||
|
||
```jsx
|
||
medusa.admin.batchJobs.retrieve(batchJobId)
|
||
.then(( batch_job ) => {
|
||
console.log(batch_job.status, batch_job.result)
|
||
})
|
||
```
|
||
|
||
</TabItem>
|
||
<TabItem value="fetch" label="Fetch API">
|
||
|
||
```jsx
|
||
fetch(`<BACKEND_URL>/admin/batch-jobs/${batchJobId}`, {
|
||
credentials: "include",
|
||
})
|
||
.then((response) => response.json())
|
||
.then(({ batch_job }) => {
|
||
console.log(batch_job.status, batch_job.result)
|
||
})
|
||
```
|
||
|
||
</TabItem>
|
||
<TabItem value="curl" label="cURL">
|
||
|
||
```bash
|
||
curl -L -X GET '<BACKEND_URL>/admin/batch-jobs/<BATCH_JOB_ID>' \
|
||
-H 'Authorization: Bearer <API_TOKEN>'
|
||
# <BATCH_JOB_ID> is the ID of the batch job
|
||
```
|
||
|
||
</TabItem>
|
||
</Tabs>
|
||
|
||
Based on the batch job strategy implemented in this documentation, the `result` property could be something like this:
|
||
|
||
```json noReport
|
||
"result": {
|
||
"count": 1,
|
||
"stat_descriptors": [
|
||
{
|
||
"key": "product-publish-count",
|
||
"name": "Number of products to publish",
|
||
"message": "1 product(s) will be published."
|
||
}
|
||
],
|
||
"advancement_count": 0
|
||
},
|
||
```
|
||
|
||
### Confirm Batch Job
|
||
|
||
To process the batch job, send a request to [confirm the batch job](https://docs.medusajs.com/api/admin#batch-jobs_postbatchjobsbatchjobconfirmprocessing):
|
||
|
||
<Tabs groupId="request-type" isCodeTabs={true}>
|
||
<TabItem value="client" label="Medusa JS Client" default>
|
||
|
||
```jsx
|
||
medusa.admin.batchJobs.confirm(batchJobId)
|
||
.then(( batch_job ) => {
|
||
console.log(batch_job.status)
|
||
})
|
||
```
|
||
|
||
</TabItem>
|
||
<TabItem value="fetch" label="Fetch API">
|
||
|
||
```jsx
|
||
fetch(`<BACKEND_URL>/admin/batch-jobs/${batchJobId}/confirm`, {
|
||
method: "POST",
|
||
credentials: "include",
|
||
})
|
||
.then((response) => response.json())
|
||
.then(({ batch_job }) => {
|
||
console.log(batch_job.status)
|
||
})
|
||
```
|
||
|
||
</TabItem>
|
||
<TabItem value="curl" label="cURL">
|
||
|
||
```bash
|
||
curl -L -X POST '<BACKEND_URL>/admin/batch-jobs/<BATCH_JOB_ID>/confirm' \
|
||
-H 'Authorization: Bearer <API_TOKEN>'
|
||
# <BATCH_JOB_ID> is the ID of the batch job
|
||
```
|
||
|
||
</TabItem>
|
||
</Tabs>
|
||
|
||
The batch job will start processing afterward. Based on the batch job strategy implemented in this documentation, draft products will be published.
|
||
|
||
You can [retrieve the batch job](#optional-retrieve-batch-job) at any given point to check its status.
|