Skip to main content

How to Create a Stock Location Service

In this document, you’ll learn how to create a stock location service, which you can use in a custom stock location module in the Medusa backend.

Overview

A stock location module is used in a commerce application, such as the Medusa backend, to handle functionalities related to the different locations a stock-kept item can be located in.

While Medusa provides a stock-location module that you can use in your Medusa backend, you can also create a custom module to handle these functionalities.

The module is expected to, at the very least, export the stock-location service. If necessary, you can include entities, migrations, and other resources as part of the export.

This guide will only explain what is required to create in your custom stock location service, and not the entities or other resources, as those you have the freedom to choose how to implement. You can refer to the Modules documentation for other details on how to create and use the module.

It should be noted that the Medusa backend expects the stock location module to have entities for a location and a location address, as it uses the IDs of those entities when orchestrating between different modules and the in the endpoints it exposes. You can learn more about this in the Stock Location Module Architecture documentation.


Prerequisites

The IStockLocationServiceCopy to Clipboard interface that you’ll be implementing is available in the @medusajs/typesCopy to Clipboard package. So, make sure to install it in your Medusa backend or the module project (depending on where you’re adding your implementation):

yarn add @medusajs/types
Report Incorrect CodeCopy to Clipboard

You’ll also be using decorators in your methods that are imported from the @medusajs/utilsCopy to Clipboard package, so make sure to install it as well:

yarn add @medusajs/utils
Report Incorrect CodeCopy to Clipboard

Step 1: Implement the Stock Location Service

Create a file in the src/servicesCopy to Clipboard directory that will hold your custom stock location service. For example, src/services/stock-location.tsCopy to Clipboard.

In that file, add the following content:

src/services/stock-location.ts
import { 
CreateStockLocationInput,
FilterableStockLocationProps,
FindConfig,
IStockLocationService,
SharedContext,
StockLocationDTO,
UpdateStockLocationInput,
} from "@medusajs/types"
import {
InjectEntityManager,
MedusaContext,
} from "@medusajs/utils"

class StockLocationService implements IStockLocationService {
async list(
selector: FilterableStockLocationProps,
config?: FindConfig<StockLocationDTO> | undefined,
context?: SharedContext | undefined
): Promise<StockLocationDTO[]> {
throw new Error("Method not implemented.")
}
async listAndCount(
selector: FilterableStockLocationProps,
config?: FindConfig<StockLocationDTO> | undefined,
context?: SharedContext | undefined
): Promise<[StockLocationDTO[], number]> {
throw new Error("Method not implemented.")
}
async retrieve(
id: string,
config?: FindConfig<StockLocationDTO> | undefined,
context?: SharedContext | undefined
): Promise<StockLocationDTO> {
throw new Error("Method not implemented.")
}
async create(
input: CreateStockLocationInput,
context?: SharedContext | undefined
): Promise<StockLocationDTO> {
throw new Error("Method not implemented.")
}
async update(
id: string,
input: UpdateStockLocationInput,
context?: SharedContext | undefined
): Promise<StockLocationDTO> {
throw new Error("Method not implemented.")
}
async delete(
id: string,
context?: SharedContext | undefined
): Promise<void> {
throw new Error("Method not implemented.")
}
}

export default StockLocationService
Report Incorrect CodeCopy to Clipboard

This defines the class StockLocationServiceCopy to Clipboard that implements the IStockLocationServiceCopy to Clipboard service imported from the @medusajs/typesCopy to Clipboard package.

The following sections explain the different methods you need to implement.

Using Method Decorators

For each of the methods, you’ll be using the following decorators:

  1. @InjectEntityManagerCopy to Clipboard: Ensures that a transaction entity manager is always passed to the method. The transaction manager is useful when performing database operations.
  2. @MedusaContextCopy to Clipboard: Used on a parameter passed to a method having the @InjectEntityManagerCopy to Clipboard decorator. It indicates which parameter should include the injected transaction manager. When used on a contextCopy to Clipboard parameter as shown below, the context is no longer optional and you can always expect the transaction manager to be passed as a parameter.

To use these decorators, it’s recommended to include the following configurations in your tsconfig.jsonCopy to Clipboard file:

{
//other configurations
"compilerOptions": {
// other configurations
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
}
}
Report Incorrect CodeCopy to Clipboard

Implementing list Method

The listCopy to Clipboard method is used to retrieve a list of stock locations. It accepts the following parameters:

  1. selectorCopy to Clipboard: This is the first parameter passed to the method, and the only required parameter. It is an object that has the following properties:
    1. idCopy to Clipboard: an optional string or array of strings indicating the IDs of locations. It is used to filter the retrieved locations by ID.
    2. nameCopy to Clipboard: an optional string, array of strings, or a StringComparisonOperatorCopy to Clipboard object that is used to search and filter locations by their name. The StringComparisonOperatorCopy to Clipboard can have the following properties:
      1. ltCopy to Clipboard: indicates a string that the name should be less than.
      2. gtCopy to Clipboard: indicates a string that the name should be greater than.
      3. gteCopy to Clipboard: indicates a string that the name should be greater than or equal to.
      4. lteCopy to Clipboard: indicates a string the name should be less than or equal to.
  2. configCopy to Clipboard: This is the second parameter and it is an optional parameter. It’s an object that can have any of the following optional properties:
    1. selectCopy to Clipboard: an array of strings indicating the attributes in your location entity to retrieve.
    2. skipCopy to Clipboard: a number indicating how many location records to skip before retrieving the list.
    3. takeCopy to Clipboard: a number indicating how many location records to retrieve.
    4. orderCopy to Clipboard: an object indicating the order to retrieve the locations by. The order is specified per attribute. So, the attribute in your entity is the property of this object, and the value of the property is either ASCCopy to Clipboard or DESCCopy to Clipboard.
  3. contextCopy to Clipboard: This is the third parameter and it’s an optional parameter. This parameter should be used to inject the transaction manager, as explained in the Method Decorators section. It’s an object that can have any of the following optional properties:
    1. transactionManagerCopy to Clipboard: the transaction manager to use to perform database operations.

This method is expected to return an array of objects of the following type:

type StockLocationDTO = {
id: string;
name: string;
metadata: Record<string, unknown> | null;
address_id: string;
address?: StockLocationAddressDTO;
created_at: string | Date;
updated_at: string | Date;
deleted_at: string | Date | null;
}
Report Incorrect CodeCopy to Clipboard

Here’s an example implementation of the method:

src/services/stock-location.ts
class StockLocationService implements IStockLocationService {
// ...
@InjectEntityManager()
async list(
selector: FilterableStockLocationProps,
config?: FindConfig<StockLocationDTO> | undefined,
@MedusaContext() context?: SharedContext | undefined
): Promise<StockLocationDTO[]> {
const manager = context.transactionManager!
const locationRepo = manager.getRepository(
CustomStockLocation
)

// TODO retrieve and return locations
// for example
return await locationRepo.find(selector)
}
}
Report Incorrect CodeCopy to Clipboard

This example shows how you can use the context to retrieve the transaction manager, then retrieve your repository that you will use to retrieve and return locations. Make sure to replace CustomStockLocationCopy to Clipboard with your stock location entity.

Implementing listAndCount Method

This method is similar to the list method, but it returns both the list of locations and a count of the locations.

It accepts the exact same parameters as the list method, but expects to return an array of two items. The first one being the list of locations, and the second one being the count of locations.

Here’s an example implementation of the method:

src/services/stock-location.ts
class StockLocationService implements IStockLocationService {
// ...
@InjectEntityManager()
async listAndCount(
selector: FilterableStockLocationProps,
config?: FindConfig<StockLocationDTO> | undefined,
@MedusaContext() context?: SharedContext | undefined
): Promise<[StockLocationDTO[], number]> {
const manager = context.transactionManager!
const locationRepo = manager.getRepository(
CustomStockLocation
)

// TODO retrieve and return locations
// for example
return await locationRepo.findAndCount(selector)
}
}
Report Incorrect CodeCopy to Clipboard

Make sure to replace CustomStockLocationCopy to Clipboard with your stock location entity.

Implementing retrieve Method

This method is used to retrieve a single location. It accepts the following parameters:

  1. idCopy to Clipboard: this is the first parameter and is required. It’s a string indicating the ID of the location to retrieve.
  2. configCopy to Clipboard: This is the second parameter and an optional parameter. It’s an object having the same properties as the configCopy to Clipboard parameter mentioned in the list method.
  3. contextCopy to Clipboard: This is the third parameter and an optional parameter. It’s of the same type as the contextCopy to Clipboard parameter mentioned in the list method.

This method returns the location as an object.

For example:

src/services/stock-location.ts
class StockLocationService implements IStockLocationService {
// ...
@InjectEntityManager()
async retrieve(
id: string,
config?: FindConfig<StockLocationDTO> | undefined,
@MedusaContext() context?: SharedContext | undefined
): Promise<StockLocationDTO> {
const manager = context.transactionManager!
const locationRepo = manager.getRepository(
CustomStockLocation
)

// TODO retrieve the location using its ID
// for example
const [location] = await locationRepo.find({
id,
})
return location
}
}
Report Incorrect CodeCopy to Clipboard

Make sure to replace CustomStockLocationCopy to Clipboard with your stock location entity.

Implementing create Method

This method is used to create a new location. It accepts the following parameters:

  1. inputCopy to Clipboard: This is the first parameter, and it’s required. It's an object holding the following properties:
    1. nameCopy to Clipboard: this property is required, and its value is the name of the location.
    2. address_idCopy to Clipboard: this property is optional, and it’s the ID of the address to associate with this location.
    3. addressCopy to Clipboard: this property is optional, and it’s an object holding address properties including address_1Copy to Clipboard, country_codeCopy to Clipboard, cityCopy to Clipboard, and more.
    4. metadataCopy to Clipboard: this property is optional, and it’s an object that should be stored in the metadataCopy to Clipboard attribute of the created location.
  2. contextCopy to Clipboard: This is the second parameter and an optional parameter. It’s of the same type as the contextCopy to Clipboard parameter mentioned in the list method.

The method is expected to return the created location as an object.

For example:

src/services/stock-location.ts
class StockLocationService implements IStockLocationService {
// ...
@InjectEntityManager()
async create(
input: CreateStockLocationInput,
@MedusaContext() context?: SharedContext | undefined
): Promise<StockLocationDTO> {
const manager = context.transactionManager!
const locationRepo = manager.getRepository(
CustomStockLocation
)

// TODO create the location and return it
// for example
return await locationRepo.create(input)
}
}
Report Incorrect CodeCopy to Clipboard

Make sure to replace CustomStockLocationCopy to Clipboard with your stock location entity.

Implementing update Method

This method is used to update a location by its ID. It accepts the following parameters:

  1. idCopy to Clipboard: this is the first parameter and is required. It’s a string indicating the ID of the location to update.
  2. inputCopy to Clipboard: this is the second parameter and is required. It’s an object having any of the following optional properties:
    1. nameCopy to Clipboard: a string indicating the name of the location.
    2. address_idCopy to Clipboard: the ID of the address to associate with this location.
    3. addressCopy to Clipboard: an object holding address properties including address_1Copy to Clipboard, country_codeCopy to Clipboard, cityCopy to Clipboard, and more.
    4. metadataCopy to Clipboard: an object that should be stored in the metadataCopy to Clipboard attribute of the created location.
  3. contextCopy to Clipboard: This is the third parameter and an optional parameter. It’s of the same type as the contextCopy to Clipboard parameter mentioned in the list method.

This method is expected to return the updated location object.

For example:

src/services/stock-location.ts
class StockLocationService implements IStockLocationService {
// ...
@InjectEntityManager()
async update(
id: string,
input: UpdateStockLocationInput,
@MedusaContext() context?: SharedContext | undefined
): Promise<StockLocationDTO> {
const manager = context.transactionManager!
const locationRepo = manager.getRepository(
CustomStockLocation
)

// TODO update the location and return it
// for example
await locationRepo.update(id, input)
return await this.retrieve(id)
}
}
Report Incorrect CodeCopy to Clipboard

Make sure to replace CustomStockLocationCopy to Clipboard with your stock location entity.

Implementing delete Method

This method is used to delete a location by its ID. It accepts the following parameters:

  1. idCopy to Clipboard: this is the first parameter and is required. It’s a string indicating the ID of the location to delete.
  2. contextCopy to Clipboard: This is the second parameter and an optional parameter. It’s of the same type as the contextCopy to Clipboard parameter mentioned in the list method.

This method is not expected to return anything.

For example:

src/services/stock-location.ts
class StockLocationService implements IStockLocationService {
// ...
@InjectEntityManager()
async delete(
id: string,
@MedusaContext() context?: SharedContext | undefined
): Promise<void> {
const manager = context.transactionManager!
const locationRepo = manager.getRepository(
CustomStockLocation
)

// TODO delete the location
// for example
await locationRepo.delete(id)
}
}
Report Incorrect CodeCopy to Clipboard

Make sure to replace CustomStockLocationCopy to Clipboard with your stock location entity.


Step 2: Use the Stock Location Service

After implementing your custom service along with any other necessary resources, you can test it out or use it in your Medusa backend. You can learn more about how to do that in the Create Module documentation.


See Also

Was this page helpful?