229 lines
7.1 KiB
Plaintext
229 lines
7.1 KiB
Plaintext
export const metadata = {
|
||
title: `${pageNumber} Data Models`,
|
||
}
|
||
|
||
# {metadata.title}
|
||
|
||
In this chapter, you’ll learn what data models are and how to create a data model.
|
||
|
||
## What is a Data Model?
|
||
|
||
A data model is a class that represents a table in the database. A data model is created in a module. You can then create a service that manages that data model.
|
||
|
||
Data models are based on [MikroORM](https://mikro-orm.io/docs/quick-start). So, you can use its decorators, types, and utilities when creating a model.
|
||
|
||
---
|
||
|
||
## How to Create a Data Model
|
||
|
||
<Note title="Steps Summary">
|
||
|
||
1. Create data model class in a module.
|
||
2. Generate migration for the data model.
|
||
3. Add migration scripts to the module's definition.
|
||
4. Run migration to add table for data model in the database.
|
||
|
||
</Note>
|
||
|
||
A data model is a class created in a TypeScript or JavaScript file created in a module under its `models` directory.
|
||
|
||
For example, create the file `src/modules/hello/models/my-custom.ts` with the following content:
|
||
|
||
```ts title="src/modules/hello/models/my-custom.ts"
|
||
import { generateEntityId } from "@medusajs/utils"
|
||
import {
|
||
BeforeCreate,
|
||
Entity,
|
||
OnInit,
|
||
PrimaryKey,
|
||
Property,
|
||
} from "@mikro-orm/core"
|
||
|
||
@Entity()
|
||
export class MyCustom {
|
||
@PrimaryKey({ columnType: "text" })
|
||
id!: string
|
||
|
||
@Property({ columnType: "text" })
|
||
name: string
|
||
|
||
@BeforeCreate()
|
||
onCreate() {
|
||
this.id = generateEntityId(this.id, "mc")
|
||
}
|
||
|
||
@OnInit()
|
||
OnInit() {
|
||
this.id = generateEntityId(this.id, "mc")
|
||
}
|
||
}
|
||
```
|
||
|
||
This defines a new data model `MyCustom` with the fields `id` and `name`.
|
||
|
||
The `onCreate` method generates an ID for the data model's record when it's created. The `onInit` method sets the ID when the record is loaded.
|
||
|
||
<Note title="Tip">
|
||
|
||
The `generateEntityId` utility method prefixes the `id` of a record with the string provided in the second parameter. This follows Medusa's conventions of creating IDs.
|
||
|
||
</Note>
|
||
|
||
### Create a Migration
|
||
|
||
After creating the data model, you must create a migration that creates a table in your database for this data model.
|
||
|
||
A migration is a class that implements an `up` and `down` method, where the `up` method reflects changes on the database, and the `down` method reverts the changes from the database.
|
||
|
||
<Details summaryContent="Generate with MikroORM">
|
||
MikroORM provides a CLI tool that helps you generate migrations. To use it:
|
||
|
||
1. Create the file `src/modules/hello/mikro-orm.config.dev.ts` with the following content:
|
||
|
||
```ts
|
||
import "dotenv/config"
|
||
import path from "path"
|
||
import { TSMigrationGenerator } from "@medusajs/utils"
|
||
import { MyCustom } from "./models/my-custom"
|
||
|
||
module.exports = {
|
||
entities: [MyCustom],
|
||
schema: "public",
|
||
clientUrl: "postgres://postgres@localhost/medusa-hello",
|
||
type: "postgresql",
|
||
migrations: {
|
||
path: path.join(__dirname, "migrations"),
|
||
generator: TSMigrationGenerator,
|
||
},
|
||
}
|
||
```
|
||
|
||
2. Run the following command in the root directory of your Medusa application:
|
||
|
||
```bash
|
||
npx cross-env MIKRO_ORM_CLI=./src/modules/hello/mikro-orm.config.dev.ts mikro-orm migration:create
|
||
```
|
||
|
||
<Note title="Tip">
|
||
|
||
You can add this command as a script in `package.json` for easy usage in the future. Use this command whenever you want to generate a new migration in your module.
|
||
|
||
</Note>
|
||
|
||
After running the command, a new file is created under the `src/modules/hello/migrations` directory. This file holds `up` and `down` methods that define the actions to execute when running and reverting the migration respectively.
|
||
|
||
</Details>
|
||
|
||
For example:
|
||
|
||
```ts
|
||
import { Migration } from "@mikro-orm/migrations"
|
||
|
||
export class Migration20240429090012 extends Migration {
|
||
|
||
async up(): Promise<void> {
|
||
this.addSql("create table if not exists \"my_custom\" (\"id\" varchar(255) not null, \"name\" text not null, constraint \"my_custom_pkey\" primary key (\"id\"));")
|
||
}
|
||
|
||
async down(): Promise<void> {
|
||
this.addSql("drop table if exists \"my_custom\" cascade;")
|
||
}
|
||
|
||
}
|
||
```
|
||
|
||
In the `up` method, you create the table `my_custom` and define its columns. In the `down` method, you drop the table.
|
||
|
||
<Note title="Tip">
|
||
|
||
The queries performed in each of the methods use PostgreSQL syntax.
|
||
|
||
</Note>
|
||
|
||
### Add Migration to Module Definition
|
||
|
||
After creating the migration, you must add it to your module's definition. Otherwise, the Medusa application won't run it.
|
||
|
||
To add a module's migrations to its definitions, use the `ModulesSdkUtils` utility functions imported from `@medusajs/utils`. It has functions to create and define the migration scripts in your module definition.
|
||
|
||
Change the content of `src/modules/hello.index.ts` that you created in a [previous chapter](../modules-and-services/page.mdx) to the following:
|
||
|
||
```ts title="src/modules/hello.index.ts" highlights={[["2"], ["6"], ["10"], ["12"], ["14"], ["18"], ["22"], ["28"], ["36"]]}
|
||
import HelloModuleService from "./service"
|
||
// add necessary imports
|
||
import { ModulesSdkUtils } from "@medusajs/utils"
|
||
import { MyCustom } from "./models/my-custom"
|
||
|
||
// define useful constants
|
||
const moduleName = "hello"
|
||
const pathToMigrations = __dirname + "/migrations"
|
||
|
||
// assemble object to pass to utility functions
|
||
const migrationScriptOptions = {
|
||
// the module's name
|
||
moduleName,
|
||
// the data models of the modules
|
||
models: {
|
||
MyCustom,
|
||
},
|
||
// the path to the migrations directory
|
||
pathToMigrations,
|
||
}
|
||
|
||
// create and export the script that runs migrations
|
||
export const runMigrations = ModulesSdkUtils
|
||
.buildMigrationScript(
|
||
migrationScriptOptions
|
||
)
|
||
|
||
// create and export the script that reverts migrations
|
||
export const revertMigration = ModulesSdkUtils
|
||
.buildRevertMigrationScript(
|
||
migrationScriptOptions
|
||
)
|
||
|
||
export default {
|
||
service: HelloModuleService,
|
||
// add the run and revert migration scripts to the module's definition
|
||
runMigrations,
|
||
revertMigration,
|
||
}
|
||
```
|
||
|
||
After importing `ModulesSdkUtils`, you use its `buildMigrationScript` function to create the script that runs the migration, and its `buildRevertMigrationScript` function to create the script that reverts the migration.
|
||
|
||
Both the `buildMigrationScript` and `buildRevertMigrationScript` accept the same object type as a parameter, which has the following properties:
|
||
|
||
- `moduleName`: The name of the module that the migrations belong to.
|
||
- `models`: An object of the module's data models.
|
||
- `pathToMigrations`: The path to the `migrations` directory.
|
||
|
||
Both created scripts must be exported in the file and within the module's definition object.
|
||
|
||
### Run Migration
|
||
|
||
To reflect the changes in the migration, transpile your source files using the `build` command, then run the `migration` command:
|
||
|
||
```bash npm2yarn
|
||
npm run build
|
||
npx medusa migrations run
|
||
```
|
||
|
||
If ran successfully, the `my_custom` table will be created in the database.
|
||
|
||
---
|
||
|
||
## When to Use
|
||
|
||
<Note title="Use data models when" type="success">
|
||
|
||
- You want to store data related to your customization in the database.
|
||
|
||
</Note>
|
||
|
||
<Note title="Don't use data models if" type="error">
|
||
|
||
- You want to store simple key-value pairs related to an existing data model. Instead, use the `metadata` field that most existing models have, which is an object of custom key-value pairs.
|
||
|
||
</Note>
|