4.3.6. Perform Database Operations in a Service

In this chapter, you'll learn how to perform database operations in a module's service.

NoteThis chapter is intended for more advanced database use-cases where you need more control over queries and operations. For basic database operations, such as creating or retrieving data of a model, use the Service Factory instead.

Run Queries#

MikroORM's entity manager is a class that has methods to run queries on the database and perform operations.

Medusa provides an InjectManager decorator imported from @medusajs/utils that injects a service's method with a forked entity manager.

So, to run database queries in a service:

  1. Add the InjectManager decorator to the method.
  2. Add as a last parameter an optional sharedContext parameter that has the MedusaContext decorator imported from @medusajs/utils. This context holds database-related context, including the manager injected by InjectManager

For example, in your service, add the following methods:

Code
1// other imports...2import { 3  InjectManager,4  MedusaContext,5} from "@medusajs/framework/utils"6
7class HelloModuleService {8  // ...9
10  @InjectManager()11  async getCount(12    @MedusaContext() sharedContext?: Context<EntityManager>13  ): Promise<number> {14    return await sharedContext.manager.count("my_custom")15  }16  17  @InjectManager()18  async getCountSql(19    @MedusaContext() sharedContext?: Context<EntityManager>20  ): Promise<number> {21    const data = await sharedContext.manager.execute(22      "SELECT COUNT(*) as num FROM my_custom"23    ) 24    25    return parseInt(data[0].num)26  }27}

You add two methods getCount and getCountSql that have the InjectManager decorator. Each of the methods also accept the sharedContext parameter which has the MedusaContext decorator.

The entity manager is injected to the sharedContext.manager property, which is an instance of EntityManager from the @mikro-orm/knex package.

You use the manager in the getCount method to retrieve the number of records in a table, and in the getCountSql to run a PostgreSQL query that retrieves the count.

NoteRefer to MikroORM's reference for a full list of the entity manager's methods.

Execute Operations in Transactions#

To wrap database operations in a transaction, you create two methods:

  1. A private or protected method that's wrapped in a transaction. To wrap it in a transaction, you use the InjectTransactionManager decorator imported from @medusajs/utils.
  2. A public method that calls the transactional method. You use on it the InjectManager decorator as explained in the previous section.

Both methods must accept as a last parameter an optional sharedContext parameter that has the MedusaContext decorator imported from @medusajs/utils. It holds database-related contexts passed through the Medusa application.

For example:

Code
1import { 2  InjectManager,3  InjectTransactionManager,4  MedusaContext,5} from "@medusajs/framework/utils"6import { Context } from "@medusajs/framework/types"7import { EntityManager } from "@mikro-orm/knex"8
9class HelloModuleService {10  // ...11  @InjectTransactionManager()12  protected async update_(13    input: {14      id: string,15      name: string16    },17    @MedusaContext() sharedContext?: Context<EntityManager>18  ): Promise<any> {19    const transactionManager = sharedContext.transactionManager20    await transactionManager.nativeUpdate(21      "my_custom",22      {23        id: input.id,24      },25      {26        name: input.name,27      }28    )29
30    // retrieve again31    const updatedRecord = await transactionManager.execute(32      `