- Get Started
- Product
- Resources
- Tools & SDKs
- Framework
- Reference
- Get Started
- Product
- Resources
- Tools & SDKs
- Framework
- Reference
2.3. Modules
In this chapter, you’ll learn about modules and how to create them.
What is a Module?#
A module is a package of reusable commerce or architectural functionalities.
In Medusa, modules handle business logic in a class called a service, and define and manage data models that represent tables in the database.
Out of the box, Medusa comes with multiple pre-built modules for core commerce needs. For example, the Cart Module holds the data models and business logic for cart operations.
As you learn more about Medusa, you will see that Modules are central to customizations and integrations.
How to Create a Module?#
In this section, you'll build a module that has a MyCustom
data model and a service to manage that data model. You'll then use the module's service in an API route to create a record of MyCustom
.
Modules are created in a sub-directory of src/modules
.
For example, create the directory src/modules/hello
.
1. Create Data Model#
A data model represents a table in the database. It's created in a TypeScript or JavaScript file under the module's models
directory.
For example, create the file src/modules/hello/models/my-custom.ts
with the following content:
You define the data model using the define
method of the model
utility imported from @medusajs/framework/utils
. It accepts two parameters:
- The first one is the name of the data model's table in the database. It should be snake-case.
- The second is an object, which is the data model's schema. The schema's properties are defined using the
model
's methods.
The example above defines the data model MyCustom
with the properties id
and name
.
2. Create Service#
A module must define a service that implements its functionalities, such as managing the records of your custom data models in the database.
A service is a TypeScript or JavaScript class defined in the service.ts
file at the root of your module's directory.
For example, create the file src/modules/hello/service.ts
with the following content:
In the snippet above, your module's service extends a class generated by the MedusaService
utility function, which is the service factory.
The MedusaService
function accepts as a parameter an object of data models, and returns a class with generated methods for data-management Create, Read, Update, and Delete (CRUD) operations on those data models.
For example, HelloModuleService
now has a createMyCustoms
method to create MyCustom
records, and retrieveMyCustom
to retrieve a MyCustom
record.
3. Export Module Definition#
A module must have an index.ts
file in its root directory. The file exports the module's definition.
For example, create the file src/modules/hello/index.ts
with the following content:
You use the Module
function imported from @medusajs/framework/utils
to create the module's definition. It accepts two parameters:
- The name that the module's main service is registered under (
helloModuleService
). - An object with a required property
service
indicating the module's main service.
4. Add Module to Configurations#
The last step is to add the module in Medusa’s configurations.
In medusa-config.ts
, add a modules
property and pass in it your custom module:
Its value is an array of objects, each having a resolve
property, whose value is either a path to the module's directory, or an npm
package’s name.
5. Generate Migrations#
A migration is a TypeScript or JavaScript file that defines database changes made by your module, such as creating the my_custom
table for the MyCustom
data model.
To generate a migration for the data models in your module, run the following command:
The db:generate
command of the Medusa CLI accepts one or more module names to generate the migration for.
The above command creates a migration file in the directory src/modules/hello/migrations
similar to the following:
1import { Migration } from "@mikro-orm/migrations"2 3export class Migration20240702105919 extends Migration {4 5 async up(): Promise<void> {6 this.addSql("create table if not exists \"my_custom\" (\"id\" text not null, \"name\" text not null, \"created_at\" timestamptz not null default now(), \"updated_at\" timestamptz not null default now(), \"deleted_at\" timestamptz null, constraint \"my_custom_pkey\" primary key (\"id\"));")7 }8 9 async down(): Promise<void> {10 this.addSql("drop table if exists \"my_custom\" cascade;")11 }12 13}
In the migration class, the up
method creates the table my_custom
and defines its columns using PostgreSQL syntax. The down
method drops the table.
6. Run Migrations#
To reflect the changes in the generated migration file, run the db:migrate
command:
This creates the my_custom
table in the database.
Test the Module#
Since the module's main service is registered in the Medusa container, you can resolve it in other resources to use its methods.
For example, create the API route src/api/custom/route.ts
with the following content:
1import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"2import HelloModuleService from "../../modules/hello/service"3import { HELLO_MODULE } from "../../modules/hello"4 5export async function GET(6 req: MedusaRequest,7 res: MedusaResponse8): Promise<void> {9 const helloModuleService: HelloModuleService = req.scope.resolve(10 HELLO_MODULE11 )12 13 const my_custom = await helloModuleService.createMyCustoms({14 name: "test",15 })16 17 res.json({18 my_custom,19 })20}
You resolve the Hello Module's main service and use its generated method createMyCustoms
to create a new record in the database, then return that record.
Then, start the Medusa application:
Finally, send a GET
request to /custom
:
You’ll receive the following response:
Why Use Modules#
In digital commerce, you often need to introduce custom behavior specific to your products, industry, tech stack, or your general ways of working. In other commerce platforms, introducing custom business logic and data models requires setting up separate applications to manage these customizations.
Medusa removes this overhead by allowing you to easily write custom Modules that integrate into the Medusa application without implications on the existing setup.
- You're adding a new table to the database.
- You're extending an existing table in the database to add custom fields, which is explained in later chapters.
- You're integrating a third-party system for commerce or architectural features, as explained in later chapters.
- You want to re-use your custom commerce functionalities across Medusa applications or use them in other environments, such as Edge functions and Next.js apps.