docs: improved data model docs (#11884)

* docs: improved data model docs

* fix build errors
This commit is contained in:
Shahed Nasser
2025-03-18 11:45:21 +02:00
committed by GitHub
parent aa6d5aa3cf
commit 3f2bdb59cb
23 changed files with 10642 additions and 10522 deletions
@@ -56,7 +56,7 @@ You define the data model using the `define` method of the DML. It accepts two p
<Note title="Tip">
Learn about other property types in [this chapter](../../../fundamentals/data-models/property-types/page.mdx).
Learn about other property types in [this chapter](../../../fundamentals/data-models/properties/page.mdx).
</Note>
@@ -1,84 +0,0 @@
export const metadata = {
title: `${pageNumber} Configure Data Model Properties`,
}
# {metadata.title}
In this chapter, youll learn how to configure data model properties.
## Propertys Default Value
Use the `default` method on a property's definition to specify the default value of a property.
For example:
export const defaultHighlights = [
["6", "default", "Set the default value to `black`."],
["9", "default", "Set the default value to `0`."]
]
```ts highlights={defaultHighlights}
import { model } from "@medusajs/framework/utils"
const MyCustom = model.define("my_custom", {
color: model
.enum(["black", "white"])
.default("black"),
age: model
.number()
.default(0),
// ...
})
export default MyCustom
```
In this example, you set the default value of the `color` enum property to `black`, and that of the `age` number property to `0`.
---
## Nullable Property
Use the `nullable` method to indicate that a propertys value can be `null`.
For example:
export const nullableHighlights = [
["4", "nullable", "Configure the `price` property to allow `null` values."]
]
```ts highlights={nullableHighlights}
import { model } from "@medusajs/framework/utils"
const MyCustom = model.define("my_custom", {
price: model.bigNumber().nullable(),
// ...
})
export default MyCustom
```
---
## Unique Property
The `unique` method indicates that a propertys value must be unique in the database through a unique index.
For example:
export const uniqueHighlights = [
["4", "unique", "Configure the `email` property to allow unique values only."]
]
```ts highlights={uniqueHighlights}
import { model } from "@medusajs/framework/utils"
const User = model.define("user", {
email: model.text().unique(),
// ...
})
export default User
```
In this example, multiple users cant have the same email.
@@ -1,13 +0,0 @@
export const metadata = {
title: `${pageNumber} Data Model Default Properties`,
}
# {metadata.title}
In this chapter, you'll learn about the properties available by default in your data model.
When you create a data model, the following properties are created for you by Medusa:
- `created_at`: A `dateTime` property that stores when a record of the data model was created.
- `updated_at`: A `dateTime` property that stores when a record of the data model was updated.
- `deleted_at`: A `dateTime` property that stores when a record of the data model was deleted. When you soft-delete a record, Medusa sets the `deleted_at` property to the current date.
@@ -6,35 +6,11 @@ export const metadata = {
In this chapter, youll learn how to define a database index on a data model.
## Define Database Index on Property
<Note>
Use the `index` method on a property's definition to define a database index.
You can also define an index on a property as explained in the [Properties chapter](../properties/page.mdx#define-database-index-on-property).
For example:
export const highlights = [
["5", "index", "Define an index on the `name` property."],
["6", '"IDX_MY_CUSTOM_NAME"', "Index name is optional."]
]
```ts highlights={highlights}
import { model } from "@medusajs/framework/utils"
const MyCustom = model.define("my_custom", {
id: model.id().primaryKey(),
name: model.text().index(
"IDX_MY_CUSTOM_NAME"
),
})
export default MyCustom
```
The `index` method optionally accepts the name of the index as a parameter.
In this example, you define an index on the `name` property.
---
</Note>
## Define Database Index on Data Model
@@ -73,7 +49,7 @@ An index can have conditions. For example:
export const conditionHighlights = [
["10", "where", "Specify conditions on properties."],
["11", "", "Create the index when `age` is `30`."]
["11", "age", "Create the index when `age` is `30`."]
]
```ts highlights={conditionHighlights}
@@ -102,7 +78,7 @@ In the example above, the composite index is created on the `name` and `age` pro
A property's condition can be a negation. For example:
export const negationHighlights = [
["12", "", "Create the index when `age` is not `null`."]
["12", "$ne", "Create the index when `age` is not `null`."]
]
```ts highlights={negationHighlights}
@@ -8,7 +8,7 @@ In this chapter, you'll learn how to infer the type of a data model.
## How to Infer Type of Data Model?
Consider you have a `MyCustom` data model. You can't reference this data model in a type, such as a workflow input or service method output types, since it's a variable.
Consider you have a `Post` data model. You can't reference this data model in a type, such as a workflow input or service method output types, since it's a variable.
Instead, Medusa provides `InferTypeOf` that transforms your data model to a type.
@@ -16,26 +16,26 @@ For example:
```ts
import { InferTypeOf } from "@medusajs/framework/types"
import { MyCustom } from "../models/my-custom" // relative path to the model
import { Post } from "../modules/blog/models/post" // relative path to the model
export type MyCustom = InferTypeOf<typeof MyCustom>
export type Post = InferTypeOf<typeof Post>
```
`InferTypeOf` accepts as a type argument the type of the data model.
Since the `MyCustom` data model is a variable, use the `typeof` operator to pass the data model as a type argument to `InferTypeOf`.
Since the `Post` data model is a variable, use the `typeof` operator to pass the data model as a type argument to `InferTypeOf`.
You can now use the `MyCustom` type to reference a data model in other types, such as in workflow inputs or service method outputs:
You can now use the `Post` type to reference a data model in other types, such as in workflow inputs or service method outputs:
```ts title="Example Service"
// other imports...
import { InferTypeOf } from "@medusajs/framework/types"
import { MyCustom } from "../models/my-custom"
import { Post } from "../models/post"
type MyCustom = InferTypeOf<typeof MyCustom>
type Post = InferTypeOf<typeof Post>
class HelloModuleService extends MedusaService({ MyCustom }) {
async doSomething(): Promise<MyCustom> {
class BlogModuleService extends MedusaService({ Post }) {
async doSomething(): Promise<Post> {
// ...
}
}
@@ -1,15 +1,114 @@
export const metadata = {
title: `${pageNumber} Data Models Advanced Guides`,
title: `${pageNumber} Data Models`,
}
# {metadata.title}
Data models are created and managed in a module. To learn how to create a data model in a custom module, refer to the [Modules chapter](../modules/page.mdx).
In this chapter, you'll learn what a data model is and how to create a data model.
In the next chapters, you'll learn about defining data models in more details. You'll learn about:
## What is a Data Model?
- The different property types available.
- How to set a property as a primary key.
- How to create and manage relationships.
- How to configure properties, such as making them nullable or searchable.
- How to manually write migrations.
A data model represents a table in the database. You create data models using Medusa's data modeling language (DML). It simplifies defining a table's columns, relations, and indexes with straightforward methods and configurations.
You create a data model in a [module](../modules/page.mdx). The module's service provides the methods to store and manage those data models. Then, you can resolve the module's service in other customizations, such as a [workflow](../workflows/page.mdx), to manage the data models' records.
---
## How to Create a Data Model
In a module, you can create a data model in a TypeScript or JavaScript file under the module's `models` directory.
So, for example, assuming you have a Blog Module at `src/modules/blog`, you can create a `Post` data model by creating the `src/modules/blog/models/post.ts` file with the following content:
![Updated directory overview after adding the data model](https://res.cloudinary.com/dza7lstvk/image/upload/v1732806790/Medusa%20Book/blog-dir-overview-1_jfvovj.jpg)
```ts title="src/modules/blog/models/post.ts"
import { model } from "@medusajs/framework/utils"
const Post = model.define("post", {
id: model.id().primaryKey(),
title: model.text(),
})
export default Post
```
You define the data model using the `define` method of the DML. It accepts two parameters:
1. The first one is the name of the data model's table in the database. Use snake-case names.
2. The second is an object, which is the data model's schema. The schema's properties are defined using the `model`'s methods, such as `text` and `id`.
- Data models automatically have the date properties `created_at`, `updated_at`, and `deleted_at`, so you don't need to add them manually.
The code snippet above defines a `Post` data model with `id` and `title` properties.
---
## Generate Migrations
After you create a data model in a module, then [register that module in your Medusa configurations](../modules/page.mdx#4-add-module-to-medusas-configurations), you must generate a migration to create the data model's table in the database.
A migration is a TypeScript or JavaScript file that defines database changes made by a module. Migrations are useful when you re-use a module or you're working in a team, so that when one member of a team makes a database change, everyone else can reflect it on their side by running the migrations.
For example, to generate a migration for the Blog Module, run the following command in your Medusa application's directory:
<Note>
If you're creating the module in a plugin, use the [plugin\:db\:generate command](!resources!/medusa-cli/commands/plugin#plugindbgenerate) instead.
</Note>
```bash
npx medusa db:generate blog
```
The `db:generate` command of the Medusa CLI accepts one or more module names to generate the migration for. It will create a migration file for the Blog Module in the directory `src/modules/blog/migrations` similar to the following:
```ts
import { Migration } from "@mikro-orm/migrations"
export class Migration20241121103722 extends Migration {
async up(): Promise<void> {
this.addSql("create table if not exists \"post\" (\"id\" text not null, \"title\" text not null, \"created_at\" timestamptz not null default now(), \"updated_at\" timestamptz not null default now(), \"deleted_at\" timestamptz null, constraint \"post_pkey\" primary key (\"id\"));")
}
async down(): Promise<void> {
this.addSql("drop table if exists \"post\" cascade;")
}
}
```
In the migration class, the `up` method creates the table `post` and defines its columns using PostgreSQL syntax. The `down` method drops the table.
### Run Migrations
To reflect the changes in the generated migration file on the database, run the `db:migrate` command:
<Note>
If you're creating the module in a plugin, run this command on the Medusa application that the plugin is installed in.
</Note>
```bash
npx medusa db:migrate
```
This creates the `post` table in the database.
### Migrations on Data Model Changes
Whenever you make a change to a data model, you must generate and run the migrations.
For example, if you add a new column to the `Post` data model, you must generate a new migration and run it.
---
## Manage Data Models
Your module's service should extend the [service factory](../modules/service-factory/page.mdx), which generates data-management methods for your module's data models.
For example, the Blog Module's service would have methods like `retrievePost` and `createPosts`.
Refer to the [Service Factory](../modules/service-factory/page.mdx) chapter to learn more about how to extend the service factory and manage data models, and refer to the [Service Factory Reference](!resources!/service-factory-reference) for the full list of generated methods and how to use them.
@@ -1,30 +0,0 @@
export const metadata = {
title: `${pageNumber} Data Models Primary Key`,
}
# {metadata.title}
In this chapter, youll learn how to configure the primary key of a data model.
## primaryKey Method
To set any `id`, `text`, or `number` property as a primary key, use the `primaryKey` method.
For example:
export const highlights = [
["4", "primaryKey", "Define the `id` property to be the data model's primary key."]
]
```ts highlights={highlights}
import { model } from "@medusajs/framework/utils"
const MyCustom = model.define("my_custom", {
id: model.id().primaryKey(),
// ...
})
export default MyCustom
```
In the example above, the `id` property is defined as the data model's primary key.
@@ -0,0 +1,412 @@
export const metadata = {
title: `${pageNumber} Data Model Properties`,
}
# {metadata.title}
In this chapter, you'll learn about the different property types you can use in a data model and how to configure a data model's properties.
## Data Model's Default Properties
By default, Medusa creates the following properties for every data model:
- `created_at`: A [dateTime](#dateTime) property that stores when a record of the data model was created.
- `updated_at`: A [dateTime](#dateTime) property that stores when a record of the data model was updated.
- `deleted_at`: A [dateTime](#dateTime) property that stores when a record of the data model was deleted. When you soft-delete a record, Medusa sets the `deleted_at` property to the current date.
---
## Property Types
This section covers the different property types you can define in a data model's schema using the `model` methods.
### id
The `id` method defines an automatically generated string ID property. The generated ID is a unique string that has a mix of letters and numbers.
For example:
export const idHighlights = [["4", ".id()", "Define an `id` property."]]
```ts highlights={idHighlights}
import { model } from "@medusajs/framework/utils"
const Post = model.define("post", {
id: model.id(),
// ...
})
export default Post
```
### text
The `text` method defines a string property.
For example:
export const textHighlights = [["4", "text", "Define a `text` property."]]
```ts highlights={textHighlights}
import { model } from "@medusajs/framework/utils"
const Post = model.define("post", {
name: model.text(),
// ...
})
export default Post
```
### number
The `number` method defines a number property.
For example:
export const numberHighlights = [["4", "number", "Define a `number` property."]]
```ts highlights={numberHighlights}
import { model } from "@medusajs/framework/utils"
const Post = model.define("post", {
age: model.number(),
// ...
})
export default Post
```
### float
<Note>
This property is only available after [Medusa v2.1.2](https://github.com/medusajs/medusa/releases/tag/v2.1.2).
</Note>
The `float` method defines a number property that allows for values with decimal places.
<Note title="Tip">
Use this property type when it's less important to have high precision for numbers with large decimal places. Alternatively, for higher percision, use the [bigNumber property](#bignumber).
</Note>
For example:
export const floatHighlights = [["4", "float", "Define a `float` property."]]
```ts highlights={floatHighlights}
import { model } from "@medusajs/framework/utils"
const Post = model.define("post", {
rating: model.float(),
// ...
})
export default Post
```
### bigNumber
The `bigNumber` method defines a number property that expects large numbers, such as prices.
<Note title="Tip">
Use this property type when it's important to have high precision for numbers with large decimal places. Alternatively, for less percision, use the [float property](#float).
</Note>
For example:
export const bigNumberHighlights = [["4", "bigNumber", "Define a `bigNumber` property."]]
```ts highlights={bigNumberHighlights}
import { model } from "@medusajs/framework/utils"
const Post = model.define("post", {
price: model.bigNumber(),
// ...
})
export default Post
```
### boolean
The `boolean` method defines a boolean property.
For example:
export const booleanHighlights = [["4", "boolean", "Define a `boolean` property."]]
```ts highlights={booleanHighlights}
import { model } from "@medusajs/framework/utils"
const Post = model.define("post", {
hasAccount: model.boolean(),
// ...
})
export default Post
```
### enum
The `enum` method defines a property whose value can only be one of the specified values.
For example:
export const enumHighlights = [["4", "enum", "Define a `enum` property."]]
```ts highlights={enumHighlights}
import { model } from "@medusajs/framework/utils"
const Post = model.define("post", {
color: model.enum(["black", "white"]),
// ...
})
export default Post
```
The `enum` method accepts an array of possible string values.
### dateTime
The `dateTime` method defines a timestamp property.
For example:
export const dateTimeHighlights = [["4", "dateTime", "Define a `dateTime` property."]]
```ts highlights={dateTimeHighlights}
import { model } from "@medusajs/framework/utils"
const Post = model.define("post", {
date_of_birth: model.dateTime(),
// ...
})
export default Post
```
### json
The `json` method defines a property whose value is a stringified JSON object.
For example:
export const jsonHighlights = [["4", "json", "Define a `json` property."]]
```ts highlights={jsonHighlights}
import { model } from "@medusajs/framework/utils"
const Post = model.define("post", {
metadata: model.json(),
// ...
})
export default Post
```
### array
The `array` method defines an array of strings property.
For example:
export const arrHightlights = [["4", "array", "Define an `array` property."]]
```ts highlights={arrHightlights}
import { model } from "@medusajs/framework/utils"
const Post = model.define("post", {
names: model.array(),
// ...
})
export default Post
```
### Properties Reference
Refer to the [Data Model Language (DML) reference](!resources!/references/data-model) for a full reference of the properties.
---
## Set Primary Key Property
To set any `id`, `text`, or `number` property as a primary key, use the `primaryKey` method.
For example:
export const highlights = [
["4", "primaryKey", "Define the `id` property to be the data model's primary key."]
]
```ts highlights={highlights}
import { model } from "@medusajs/framework/utils"
const Post = model.define("post", {
id: model.id().primaryKey(),
// ...
})
export default Post
```
In the example above, the `id` property is defined as the data model's primary key.
---
## Property Default Value
Use the `default` method on a property's definition to specify the default value of a property.
For example:
export const defaultHighlights = [
["6", "default", "Set the default value to `black`."],
["9", "default", "Set the default value to `0`."]
]
```ts highlights={defaultHighlights}
import { model } from "@medusajs/framework/utils"
const Post = model.define("post", {
color: model
.enum(["black", "white"])
.default("black"),
age: model
.number()
.default(0),
// ...
})
export default Post
```
In this example, you set the default value of the `color` enum property to `black`, and that of the `age` number property to `0`.
---
## Make Property Optional
Use the `nullable` method to indicate that a propertys value can be `null`. This is useful when you want a property to be optional.
For example:
export const nullableHighlights = [
["4", "nullable", "Configure the `price` property to allow `null` values."]
]
```ts highlights={nullableHighlights}
import { model } from "@medusajs/framework/utils"
const Post = model.define("post", {
price: model.bigNumber().nullable(),
// ...
})
export default Post
```
In the example above, the `price` property is configured to allow `null` values, making it optional.
---
## Unique Property
The `unique` method indicates that a propertys value must be unique in the database through a unique index.
For example:
export const uniqueHighlights = [
["4", "unique", "Configure the `email` property to allow unique values only."]
]
```ts highlights={uniqueHighlights}
import { model } from "@medusajs/framework/utils"
const User = model.define("user", {
email: model.text().unique(),
// ...
})
export default User
```
In this example, multiple users cant have the same email.
---
## Define Database Index on Property
Use the `index` method on a property's definition to define a database index.
For example:
export const dbIndexHighlights = [
["5", "index", "Define an index on the `name` property."],
["6", '"IDX_MY_CUSTOM_NAME"', "Index name is optional."]
]
```ts highlights={dbIndexHighlights}
import { model } from "@medusajs/framework/utils"
const Post = model.define("post", {
id: model.id().primaryKey(),
name: model.text().index(
"IDX_MY_CUSTOM_NAME"
),
})
export default Post
```
The `index` method optionally accepts the name of the index as a parameter.
In this example, you define an index on the `name` property.
---
## Define a Searchable Property
Methods generated by the [service factory](../../modules/service-factory/page.mdx) that accept filters, such as `list{ModelName}s`, accept a `q` property as part of the filters.
When the `q` filter is passed, the data model's searchable properties are queried to find matching records.
Use the `searchable` method on a `text` property to indicate that it's searchable.
For example:
export const searchableHighlights = [
["4", "searchable", "Define the `title` property as searchable."]
]
```ts highlights={searchableHighlights}
import { model } from "@medusajs/framework/utils"
const Post = model.define("post", {
title: model.text().searchable(),
// ...
})
export default Post
```
In this example, the `title` property is searchable.
### Search Example
If you pass a `q` filter to the `listPosts` method:
```ts
const posts = await blogModuleService.listPosts({
q: "New Products",
})
```
This retrieves records that include `New Products` in their `title` property.
@@ -1,241 +0,0 @@
export const metadata = {
title: `${pageNumber} Data Model Property Types`,
}
# {metadata.title}
In this chapter, youll learn about the types of properties in a data models schema.
## id
The `id` method defines an automatically generated string ID property. The generated ID is a unique string that has a mix of letters and numbers.
For example:
export const idHighlights = [["4", ".id()", "Define an `id` property."]]
```ts highlights={idHighlights}
import { model } from "@medusajs/framework/utils"
const MyCustom = model.define("my_custom", {
id: model.id(),
// ...
})
export default MyCustom
```
---
## text
The `text` method defines a string property.
For example:
export const textHighlights = [["4", "text", "Define a `text` property."]]
```ts highlights={textHighlights}
import { model } from "@medusajs/framework/utils"
const MyCustom = model.define("my_custom", {
name: model.text(),
// ...
})
export default MyCustom
```
---
## number
The `number` method defines a number property.
For example:
export const numberHighlights = [["4", "number", "Define a `number` property."]]
```ts highlights={numberHighlights}
import { model } from "@medusajs/framework/utils"
const MyCustom = model.define("my_custom", {
age: model.number(),
// ...
})
export default MyCustom
```
---
## float
<Note>
This property is only available after [Medusa v2.1.2](https://github.com/medusajs/medusa/releases/tag/v2.1.2).
</Note>
The `float` method defines a number property that allows for values with decimal places.
<Note title="Tip">
Use this property type when it's less important to have high precision for numbers with large decimal places. Alternatively, for higher percision, use the [bigNumber property](#bignumber).
</Note>
For example:
export const floatHighlights = [["4", "float", "Define a `float` property."]]
```ts highlights={floatHighlights}
import { model } from "@medusajs/framework/utils"
const MyCustom = model.define("my_custom", {
rating: model.float(),
// ...
})
export default MyCustom
```
---
## bigNumber
The `bigNumber` method defines a number property that expects large numbers, such as prices.
<Note title="Tip">
Use this property type when it's important to have high precision for numbers with large decimal places. Alternatively, for less percision, use the [float property](#float).
</Note>
For example:
export const bigNumberHighlights = [["4", "bigNumber", "Define a `bigNumber` property."]]
```ts highlights={bigNumberHighlights}
import { model } from "@medusajs/framework/utils"
const MyCustom = model.define("my_custom", {
price: model.bigNumber(),
// ...
})
export default MyCustom
```
---
## boolean
The `boolean` method defines a boolean property.
For example:
export const booleanHighlights = [["4", "boolean", "Define a `boolean` property."]]
```ts highlights={booleanHighlights}
import { model } from "@medusajs/framework/utils"
const MyCustom = model.define("my_custom", {
hasAccount: model.boolean(),
// ...
})
export default MyCustom
```
---
### enum
The `enum` method defines a property whose value can only be one of the specified values.
For example:
export const enumHighlights = [["4", "enum", "Define a `enum` property."]]
```ts highlights={enumHighlights}
import { model } from "@medusajs/framework/utils"
const MyCustom = model.define("my_custom", {
color: model.enum(["black", "white"]),
// ...
})
export default MyCustom
```
The `enum` method accepts an array of possible string values.
---
## dateTime
The `dateTime` method defines a timestamp property.
For example:
export const dateTimeHighlights = [["4", "dateTime", "Define a `dateTime` property."]]
```ts highlights={dateTimeHighlights}
import { model } from "@medusajs/framework/utils"
const MyCustom = model.define("my_custom", {
date_of_birth: model.dateTime(),
// ...
})
export default MyCustom
```
---
## json
The `json` method defines a property whose value is a stringified JSON object.
For example:
export const jsonHighlights = [["4", "json", "Define a `json` property."]]
```ts highlights={jsonHighlights}
import { model } from "@medusajs/framework/utils"
const MyCustom = model.define("my_custom", {
metadata: model.json(),
// ...
})
export default MyCustom
```
---
## array
The `array` method defines an array of strings property.
For example:
export const arrHightlights = [["4", "array", "Define an `array` property."]]
```ts highlights={arrHightlights}
import { model } from "@medusajs/framework/utils"
const MyCustom = model.define("my_custom", {
names: model.array(),
// ...
})
export default MyCustom
```
---
## Properties Reference
Refer to the [Data Model API reference](!resources!/references/data-model) for a full reference of the properties.
@@ -67,7 +67,7 @@ The `belongsTo` method also requires passing as a second parameter an object wit
### Optional Relationship
To make the relationship optional on the `hasOne` or `belongsTo` side, use the `nullable` method on either property as explained in [this chapter](../configure-properties/page.mdx#nullable-property).
To make the relationship optional on the `hasOne` or `belongsTo` side, use the `nullable` method on either property as explained in [this chapter](../properties/page.mdx#make-property-optional).
### One-sided One-to-One Relationship
@@ -142,7 +142,7 @@ In this example, a store has many products, but a product belongs to one store.
### Optional Relationship
To make the relationship optional on the `belongsTo` side, use the `nullable` method on the property as explained in [this chapter](../configure-properties/page.mdx#nullable-property).
To make the relationship optional on the `belongsTo` side, use the `nullable` method on the property as explained in [this chapter](../properties/page.mdx#make-property-optional).
### One-to-Many Relationship in the Database
@@ -1,50 +0,0 @@
export const metadata = {
title: `${pageNumber} Searchable Data Model Property`,
}
# {metadata.title}
In this chapter, you'll learn what a searchable property is and how to define it.
## What is a Searchable Property?
Methods generated by the [service factory](../../modules/service-factory/page.mdx) that accept filters, such as `list{ModelName}s`, accept a `q` property as part of the filters.
When the `q` filter is passed, the data model's searchable properties are queried to find matching records.
---
## Define a Searchable Property
Use the `searchable` method on a `text` property to indicate that it's searchable.
For example:
export const searchableHighlights = [
["4", "searchable", "Define the `name` property as searchable."]
]
```ts highlights={searchableHighlights}
import { model } from "@medusajs/framework/utils"
const MyCustom = model.define("my_custom", {
name: model.text().searchable(),
// ...
})
export default MyCustom
```
In this example, the `name` property is searchable.
### Search Example
If you pass a `q` filter to the `listMyCustoms` method:
```ts
const myCustoms = await helloModuleService.listMyCustoms({
q: "John",
})
```
This retrieves records that include `John` in their `name` property.
@@ -1,32 +1,46 @@
export const metadata = {
title: `${pageNumber} Write Migration`,
title: `${pageNumber} Migrations`,
}
# {metadata.title}
In this chapter, you'll learn how to create a migration and write it manually.
In this chapter, you'll learn what a migration is and how to generate a migration or write it manually.
## What is a Migration?
A migration is a class created in a TypeScript or JavaScript file under a module's `migrations` directory. It has two methods:
A migration is a TypeScript or JavaScript file that defines database changes made by a module. Migrations are useful when you re-use a module or you're working in a team, so that when one member of a team makes a database change, everyone else can reflect it on their side by running the migrations.
The migration's file has a class with two methods:
- The `up` method reflects changes on the database.
- The `down` method reverts the changes made in the `up` method.
---
## How to Write a Migration?
## Generate Migration
The Medusa CLI tool provides a [db:generate](!resources!/medusa-cli/commands/db#dbgenerate) command to generate a migration for the specified modules' data models.
Instead of you writing the migration manually, the Medusa CLI tool provides a [db:generate](!resources!/medusa-cli/commands/db#dbgenerate) command to generate a migration for a modules' data models.
Alternatively, you can manually create a migration file under the `migrations` directory of your module.
For example, assuming you have a `blog` Module, you can generate a migration for it by running the following command:
```bash
npx medusa db:generate blog
```
This generates a migration file under the `migrations` directory of the Blog Module. You can then run it to reflect the changes in the database as mentioned in [this section](#run-the-migration).
---
## Write a Migration Manually
You can also write migrations manually. To do that, create a file in the `migrations` directory of the module and in it, a class that has an `up` and `down` method. The class's name should be of the format `Migration{YEAR}{MONTH}{DAY}{HOUR}{MINUTE}.ts` to ensure migrations are ran in the correct order.
For example:
```ts title="src/modules/blog/migrations/Migration20240429.ts"
import { Migration } from "@mikro-orm/migrations"
export class Migration20240702105919 extends Migration {
export class Migration202507021059 extends Migration {
async up(): Promise<void> {
this.addSql("create table if not exists \"author\" (\"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 \"author_pkey\" primary key (\"id\"));")
@@ -39,9 +53,7 @@ export class Migration20240702105919 extends Migration {
}
```
The migration's file name should be of the format `Migration{YEAR}{MONTH}{DAY}.ts`. The migration class in the file extends the `Migration` class imported from `@mikro-orm/migrations`.
In the `up` and `down` method of the migration class, you use the `addSql` method provided by MikroORM's `Migration` class to run PostgreSQL syntax.
The migration class in the file extends the `Migration` class imported from `@mikro-orm/migrations`. In the `up` and `down` method of the migration class, you use the `addSql` method provided by MikroORM's `Migration` class to run PostgreSQL syntax.
In the example above, the `up` method creates the table `author`, and the `down` method drops the table if the migration is reverted.
@@ -65,7 +65,7 @@ You define the data model using the `define` method of the DML. It accepts two p
<Note title="Tip">
Learn about other property types in [this chapter](../../fundamentals/data-models/property-types/page.mdx).
Learn about other property types in [this chapter](../../fundamentals/data-models/properties/page.mdx#property-types).
</Note>
@@ -228,7 +228,7 @@ For example:
export const failureStatusHighlights = [
["17", "transactionId", "Receive the workflow execution's transaction ID as an input to the step."],
["20", "resolve", "Resolve the workflow engine's main service."],
["24", "setStepSuccess", "Change the step's status to successful."],
["24", "setStepFailure", "Change the step's status to failed."],
["28", "stepId", "The ID of the step as passed to `createStep`'s first parameter when it was created."],
["29", "workflowId", "The ID of the workflow as passed to `createWorkflow`'s first parameter when it was created."],
["31", "stepResponse", "The response returned by the step, since an `async` step can't return a response in its definition."]
@@ -291,8 +291,9 @@ To retrieve the workflow execution's details at a later point, you must enable [
For example:
export const highlights = [
["18", "resolve", "Resolve the workflow engine from the Medusa container."],
["30", "subscribe", "Subscribe to status changes of the workflow execution."],
["11", "resolve", "Resolve the workflow engine from the Medusa container."],
["23", "subscribe", "Subscribe to status changes of the workflow execution."],
["29", "unsubscribe", "Unsubscribe once you have the result"]
]
```ts title="src/api/workflows/route.ts" highlights={highlights} collapsibleLines="1-11" expandButtonLabel="Show Imports"
+10 -14
View File
@@ -22,29 +22,26 @@ export const generatedEditDates = {
"app/learn/fundamentals/workflows/execute-another-workflow/page.mdx": "2024-12-09T15:56:22.895Z",
"app/learn/fundamentals/modules/loaders/page.mdx": "2024-12-09T10:32:29.221Z",
"app/learn/fundamentals/admin/widgets/page.mdx": "2024-12-09T16:43:24.260Z",
"app/learn/fundamentals/data-models/page.mdx": "2024-12-09T11:34:51.065Z",
"app/learn/fundamentals/data-models/page.mdx": "2025-03-18T07:55:56.252Z",
"app/learn/fundamentals/modules/remote-link/page.mdx": "2024-09-30T08:43:53.127Z",
"app/learn/fundamentals/api-routes/protected-routes/page.mdx": "2025-03-17T11:47:10.101Z",
"app/learn/fundamentals/workflows/add-workflow-hook/page.mdx": "2024-12-09T14:42:39.693Z",
"app/learn/fundamentals/events-and-subscribers/data-payload/page.mdx": "2024-10-21T13:30:21.369Z",
"app/learn/fundamentals/data-models/default-properties/page.mdx": "2024-10-21T13:30:21.368Z",
"app/learn/fundamentals/workflows/advanced-example/page.mdx": "2024-09-11T10:46:59.975Z",
"app/learn/fundamentals/events-and-subscribers/emit-event/page.mdx": "2024-11-25T16:19:32.168Z",
"app/learn/fundamentals/workflows/conditions/page.mdx": "2025-01-27T08:45:19.027Z",
"app/learn/fundamentals/modules/module-link-directions/page.mdx": "2024-07-24T09:16:01+02:00",
"app/learn/fundamentals/admin/page.mdx": "2024-10-23T07:08:55.898Z",
"app/learn/fundamentals/workflows/long-running-workflow/page.mdx": "2025-01-27T08:45:19.028Z",
"app/learn/fundamentals/workflows/long-running-workflow/page.mdx": "2025-03-18T08:02:14.085Z",
"app/learn/fundamentals/workflows/constructor-constraints/page.mdx": "2025-02-12T13:55:33.437Z",
"app/learn/fundamentals/data-models/write-migration/page.mdx": "2025-03-11T12:58:05.655Z",
"app/learn/fundamentals/data-models/write-migration/page.mdx": "2025-03-18T08:00:44.980Z",
"app/learn/fundamentals/data-models/manage-relationships/page.mdx": "2025-02-11T15:53:12.541Z",
"app/learn/fundamentals/modules/remote-query/page.mdx": "2024-07-21T21:20:24+02:00",
"app/learn/fundamentals/modules/options/page.mdx": "2025-02-12T16:00:28.484Z",
"app/learn/fundamentals/data-models/relationships/page.mdx": "2025-02-03T08:01:18.094Z",
"app/learn/fundamentals/data-models/relationships/page.mdx": "2025-03-18T07:52:07.421Z",
"app/learn/fundamentals/workflows/compensation-function/page.mdx": "2024-12-06T14:34:50.384Z",
"app/learn/fundamentals/modules/service-factory/page.mdx": "2024-10-21T13:30:21.371Z",
"app/learn/fundamentals/data-models/primary-key/page.mdx": "2024-10-21T13:30:21.368Z",
"app/learn/fundamentals/modules/module-links/page.mdx": "2024-09-30T08:43:53.126Z",
"app/learn/fundamentals/data-models/searchable-property/page.mdx": "2024-10-21T13:30:21.369Z",
"app/learn/fundamentals/scheduled-jobs/execution-number/page.mdx": "2024-10-21T13:30:21.371Z",
"app/learn/fundamentals/api-routes/parameters/page.mdx": "2025-02-14T08:34:03.184Z",
"app/learn/fundamentals/api-routes/http-methods/page.mdx": "2024-10-21T13:30:21.367Z",
@@ -53,10 +50,8 @@ export const generatedEditDates = {
"app/learn/fundamentals/admin/ui-routes/page.mdx": "2025-02-24T09:35:11.752Z",
"app/learn/fundamentals/api-routes/middlewares/page.mdx": "2025-03-04T10:16:15.029Z",
"app/learn/fundamentals/modules/isolation/page.mdx": "2024-12-09T11:02:38.087Z",
"app/learn/fundamentals/data-models/configure-properties/page.mdx": "2024-10-21T13:30:21.368Z",
"app/learn/fundamentals/data-models/index/page.mdx": "2024-10-21T13:30:21.368Z",
"app/learn/fundamentals/data-models/index/page.mdx": "2025-03-18T07:59:07.798Z",
"app/learn/fundamentals/custom-cli-scripts/page.mdx": "2024-10-23T07:08:55.898Z",
"app/learn/fundamentals/data-models/property-types/page.mdx": "2024-12-12T10:41:32.999Z",
"app/learn/debugging-and-testing/testing-tools/integration-tests/api-routes/page.mdx": "2025-02-13T16:39:29.636Z",
"app/learn/debugging-and-testing/testing-tools/integration-tests/page.mdx": "2024-12-09T15:52:01.019Z",
"app/learn/debugging-and-testing/testing-tools/integration-tests/workflows/page.mdx": "2025-02-11T15:56:03.835Z",
@@ -76,12 +71,12 @@ export const generatedEditDates = {
"app/learn/fundamentals/module-links/query/page.mdx": "2025-03-11T15:35:10.605Z",
"app/learn/fundamentals/modules/db-operations/page.mdx": "2024-12-09T14:40:50.581Z",
"app/learn/fundamentals/modules/multiple-services/page.mdx": "2024-10-21T13:30:21.370Z",
"app/learn/fundamentals/modules/page.mdx": "2025-03-11T13:00:37.391Z",
"app/learn/fundamentals/modules/page.mdx": "2025-03-18T07:51:09.049Z",
"app/learn/debugging-and-testing/instrumentation/page.mdx": "2025-02-24T08:12:53.132Z",
"app/learn/fundamentals/api-routes/additional-data/page.mdx": "2025-01-27T08:45:19.025Z",
"app/learn/fundamentals/workflows/variable-manipulation/page.mdx": "2025-01-27T08:45:19.029Z",
"app/learn/customization/custom-features/api-route/page.mdx": "2024-12-09T10:39:30.046Z",
"app/learn/customization/custom-features/module/page.mdx": "2024-12-09T14:36:02.100Z",
"app/learn/customization/custom-features/module/page.mdx": "2025-03-18T07:49:30.590Z",
"app/learn/customization/custom-features/workflow/page.mdx": "2024-12-09T14:36:29.482Z",
"app/learn/customization/extend-features/extend-create-product/page.mdx": "2025-01-06T11:18:58.250Z",
"app/learn/customization/custom-features/page.mdx": "2024-12-09T10:46:28.593Z",
@@ -98,7 +93,7 @@ export const generatedEditDates = {
"app/learn/customization/next-steps/page.mdx": "2024-12-06T14:34:53.356Z",
"app/learn/fundamentals/modules/architectural-modules/page.mdx": "2024-10-21T13:30:21.367Z",
"app/learn/introduction/architecture/page.mdx": "2025-03-12T14:39:09.724Z",
"app/learn/fundamentals/data-models/infer-type/page.mdx": "2024-12-09T15:54:08.713Z",
"app/learn/fundamentals/data-models/infer-type/page.mdx": "2025-03-18T07:41:01.936Z",
"app/learn/fundamentals/custom-cli-scripts/seed-data/page.mdx": "2024-12-09T14:38:06.385Z",
"app/learn/fundamentals/environment-variables/page.mdx": "2025-03-11T08:55:03.343Z",
"app/learn/build/page.mdx": "2024-12-09T11:05:17.383Z",
@@ -122,5 +117,6 @@ export const generatedEditDates = {
"app/learn/configurations/medusa-config/page.mdx": "2025-03-11T14:27:04.528Z",
"app/learn/configurations/ts-aliases/page.mdx": "2025-02-11T16:57:46.683Z",
"app/learn/production/worker-mode/page.mdx": "2025-03-11T15:21:50.906Z",
"app/learn/fundamentals/module-links/read-only/page.mdx": "2025-03-17T14:18:29.924Z"
"app/learn/fundamentals/module-links/read-only/page.mdx": "2025-03-17T14:18:29.924Z",
"app/learn/fundamentals/data-models/properties/page.mdx": "2025-03-18T07:57:17.826Z"
}
+14 -54
View File
@@ -456,42 +456,12 @@ export const generatedSidebars = [
"loaded": true,
"isPathHref": true,
"type": "link",
"path": "/learn/fundamentals/data-models/property-types",
"title": "Property Types",
"path": "/learn/fundamentals/data-models/properties",
"title": "Properties",
"children": [],
"chapterTitle": "3.4.2. Property Types",
"chapterTitle": "3.4.2. Properties",
"number": "3.4.2."
},
{
"loaded": true,
"isPathHref": true,
"type": "link",
"path": "/learn/fundamentals/data-models/primary-key",
"title": "Primary Key",
"children": [],
"chapterTitle": "3.4.3. Primary Key",
"number": "3.4.3."
},
{
"loaded": true,
"isPathHref": true,
"type": "link",
"path": "/learn/fundamentals/data-models/default-properties",
"title": "Default Properties",
"children": [],
"chapterTitle": "3.4.4. Default Properties",
"number": "3.4.4."
},
{
"loaded": true,
"isPathHref": true,
"type": "link",
"path": "/learn/fundamentals/data-models/configure-properties",
"title": "Configure Properties",
"children": [],
"chapterTitle": "3.4.5. Configure Properties",
"number": "3.4.5."
},
{
"loaded": true,
"isPathHref": true,
@@ -499,8 +469,8 @@ export const generatedSidebars = [
"path": "/learn/fundamentals/data-models/relationships",
"title": "Relationships",
"children": [],
"chapterTitle": "3.4.6. Relationships",
"number": "3.4.6."
"chapterTitle": "3.4.3. Relationships",
"number": "3.4.3."
},
{
"loaded": true,
@@ -509,8 +479,8 @@ export const generatedSidebars = [
"path": "/learn/fundamentals/data-models/manage-relationships",
"title": "Manage Relationships",
"children": [],
"chapterTitle": "3.4.7. Manage Relationships",
"number": "3.4.7."
"chapterTitle": "3.4.4. Manage Relationships",
"number": "3.4.4."
},
{
"loaded": true,
@@ -519,8 +489,8 @@ export const generatedSidebars = [
"path": "/learn/fundamentals/data-models/index",
"title": "Define Index",
"children": [],
"chapterTitle": "3.4.8. Define Index",
"number": "3.4.8."
"chapterTitle": "3.4.5. Define Index",
"number": "3.4.5."
},
{
"loaded": true,
@@ -529,28 +499,18 @@ export const generatedSidebars = [
"path": "/learn/fundamentals/data-models/check-constraints",
"title": "Check Constraints",
"children": [],
"chapterTitle": "3.4.9. Check Constraints",
"number": "3.4.9."
},
{
"loaded": true,
"isPathHref": true,
"type": "link",
"path": "/learn/fundamentals/data-models/searchable-property",
"title": "Searchable Property",
"children": [],
"chapterTitle": "3.4.10. Searchable Property",
"number": "3.4.10."
"chapterTitle": "3.4.6. Check Constraints",
"number": "3.4.6."
},
{
"loaded": true,
"isPathHref": true,
"type": "link",
"path": "/learn/fundamentals/data-models/write-migration",
"title": "Write Migration",
"title": "Migrations",
"children": [],
"chapterTitle": "3.4.11. Write Migration",
"number": "3.4.11."
"chapterTitle": "3.4.7. Migrations",
"number": "3.4.7."
}
],
"chapterTitle": "3.4. Data Models",
File diff suppressed because it is too large Load Diff
+3 -23
View File
@@ -244,23 +244,8 @@ export const sidebars = [
},
{
type: "link",
path: "/learn/fundamentals/data-models/property-types",
title: "Property Types",
},
{
type: "link",
path: "/learn/fundamentals/data-models/primary-key",
title: "Primary Key",
},
{
type: "link",
path: "/learn/fundamentals/data-models/default-properties",
title: "Default Properties",
},
{
type: "link",
path: "/learn/fundamentals/data-models/configure-properties",
title: "Configure Properties",
path: "/learn/fundamentals/data-models/properties",
title: "Properties",
},
{
type: "link",
@@ -282,15 +267,10 @@ export const sidebars = [
path: "/learn/fundamentals/data-models/check-constraints",
title: "Check Constraints",
},
{
type: "link",
path: "/learn/fundamentals/data-models/searchable-property",
title: "Searchable Property",
},
{
type: "link",
path: "/learn/fundamentals/data-models/write-migration",
title: "Write Migration",
title: "Migrations",
},
],
},
+25
View File
@@ -128,6 +128,31 @@ const redirects = async () => {
destination: "/learn/configurations/:path*",
permanent: true,
},
{
source: "/learn/fundamentals/data-models/configure-properties",
destination: "/learn/fundamentals/data-models/properties",
permanent: true,
},
{
source: "/learn/fundamentals/data-models/default-properties",
destination: "/learn/fundamentals/data-models/properties",
permanent: true,
},
{
source: "/learn/fundamentals/data-models/primary-key",
destination: "/learn/fundamentals/data-models/properties",
permanent: true,
},
{
source: "/learn/fundamentals/data-models/property-types",
destination: "/learn/fundamentals/data-models/properties",
permanent: true,
},
{
source: "/learn/fundamentals/data-models/searchable-property",
destination: "/learn/fundamentals/data-models/properties",
permanent: true,
},
]
}
+8 -8
View File
@@ -1063,7 +1063,7 @@ const MyCustom = model.define("my_custom", {
})
```
Learn more in [this documentation](!docs!/learn/fundamentals/data-models/property-types).
Learn more in [this documentation](!docs!/learn/fundamentals/data-models/properties).
### Set Primary Key
@@ -1106,7 +1106,7 @@ const MyCustom = model.define("my_custom", {
export default MyCustom
```
Learn more in [this documentation](!docs!/learn/fundamentals/data-models/primary-key).
Learn more in [this documentation](!docs!/learn/fundamentals/data-models/properties#set-primary-key-property).
### Default Property Value
@@ -1128,7 +1128,7 @@ const MyCustom = model.define("my_custom", {
export default MyCustom
```
Learn more in [this documentation](!docs!/learn/fundamentals/data-models/configure-properties).
Learn more in [this documentation](!docs!/learn/fundamentals/data-models/properties#property-default-value).
### Nullable Property
@@ -1145,7 +1145,7 @@ const MyCustom = model.define("my_custom", {
export default MyCustom
```
Learn more in [this documentation](!docs!/learn/fundamentals/data-models/configure-properties#nullable-property).
Learn more in [this documentation](!docs!/learn/fundamentals/data-models/properties#make-property-optional).
### Unique Property
@@ -1162,7 +1162,7 @@ const User = model.define("user", {
export default User
```
Learn more in [this documentation](!docs!/learn/fundamentals/data-models/configure-properties#unique-property).
Learn more in [this documentation](!docs!/learn/fundamentals/data-models/properties#unique-property).
### Define Database Index on Property
@@ -1181,7 +1181,7 @@ const MyCustom = model.define("my_custom", {
export default MyCustom
```
Learn more in [this documentation](!docs!/learn/fundamentals/data-models/index#define-database-index-on-property).
Learn more in [this documentation](!docs!/learn/fundamentals/data-models/properties#define-database-index-on-property).
### Define Composite Index on Data Model
@@ -1208,7 +1208,7 @@ const MyCustom = model.define("my_custom", {
export default MyCustom
```
Learn more in [this documentation](!docs!/learn/fundamentals/data-models/index#define-database-index-on-data-model).
Learn more in [this documentation](!docs!/learn/fundamentals/data-models/index).
### Make a Property Searchable
@@ -1239,7 +1239,7 @@ const myCustoms = await helloModuleService.listMyCustoms({
})
```
Learn more in [this documentation](!docs!/learn/fundamentals/data-models/searchable-property).
Learn more in [this documentation](!docs!/learn/fundamentals/data-models/properties#searchable-property).
### Create One-to-One Relationship
@@ -254,7 +254,7 @@ The `Wishlist` model has the following properties:
<Note>
Learn more about data model [properties](!docs!/learn/fundamentals/data-models/property-types) and [relations](!docs!/learn/fundamentals/data-models/relationships).
Learn more about data model [properties](!docs!/learn/fundamentals/data-models/properties) and [relations](!docs!/learn/fundamentals/data-models/relationships).
</Note>
@@ -169,7 +169,7 @@ In the data model, you define the following properties:
<Note>
Learn more about data model [properties](!docs!/learn/fundamentals/data-models/property-types) and [relations](!docs!/learn/fundamentals/data-models/relationships).
Learn more about data model [properties](!docs!/learn/fundamentals/data-models/properties) and [relations](!docs!/learn/fundamentals/data-models/relationships).
</Note>
@@ -150,7 +150,7 @@ You define the following properties for the `Vendor` data model:
<Note>
Learn more about data model [properties](!docs!/learn/fundamentals/data-models/property-types) and [relations](!docs!/learn/fundamentals/data-models/relationships).
Learn more about data model [properties](!docs!/learn/fundamentals/data-models/properties) and [relations](!docs!/learn/fundamentals/data-models/relationships).
</Note>