diff --git a/www/apps/api-reference/generated/generated-admin-sidebar.mjs b/www/apps/api-reference/generated/generated-admin-sidebar.mjs
index 6b668773db..85fa41cef3 100644
--- a/www/apps/api-reference/generated/generated-admin-sidebar.mjs
+++ b/www/apps/api-reference/generated/generated-admin-sidebar.mjs
@@ -20,6 +20,12 @@ const generatedgeneratedAdminSidebarSidebar = {
"path": "http-compression",
"loaded": true
},
+ {
+ "type": "link",
+ "title": "Manage Metadata",
+ "path": "manage-metadata",
+ "loaded": true
+ },
{
"type": "link",
"title": "Select Fields and Relations",
diff --git a/www/apps/api-reference/generated/generated-store-sidebar.mjs b/www/apps/api-reference/generated/generated-store-sidebar.mjs
index 556852701d..94325bcb4b 100644
--- a/www/apps/api-reference/generated/generated-store-sidebar.mjs
+++ b/www/apps/api-reference/generated/generated-store-sidebar.mjs
@@ -26,6 +26,12 @@ const generatedgeneratedStoreSidebarSidebar = {
"path": "http-compression",
"loaded": true
},
+ {
+ "type": "link",
+ "title": "Manage Metadata",
+ "path": "manage-metadata",
+ "loaded": true
+ },
{
"type": "link",
"title": "Select Fields and Relations",
diff --git a/www/apps/api-reference/markdown/admin.mdx b/www/apps/api-reference/markdown/admin.mdx
index b866601c15..e26980bc4b 100644
--- a/www/apps/api-reference/markdown/admin.mdx
+++ b/www/apps/api-reference/markdown/admin.mdx
@@ -483,6 +483,226 @@ x-no-compression: false
+## Manage Metadata
+
+Many data models in Medusa, such as products and carts, have a `metadata` field that allows you to store custom information in key-value pairs.
+
+When setting or updating the `metadata` field using the relevant API routes, Medusa will merge the new metadata with the existing metadata.
+
+
+
+The instructions in this section apply to any [JSON property in a data model](!docs!/learn/fundamentals/data-models/json-properties).
+
+
+
+
+
+
+
+
+
+
+
+### Accepted Values in Metadata
+
+The `metadata` is an object of key-value pairs, where the keys are strings and the values can be one of the following types:
+
+- String
+ - An empty string deletes the property from the metadata.
+- Number
+- Boolean
+- Date
+- Object
+- Arrays of any of the above types
+
+The `metadata` is not validated, so you can store any custom data in it.
+
+
+
+
+
+```ts title="Metadata Example"
+{
+ "metadata": {
+ "category": "electronics",
+ "views": 1500,
+ "is_featured": true,
+ "tags": ["new", "sale"],
+ "details": {
+ "warranty": "2 years",
+ "origin": "USA"
+ }
+ }
+}
+```
+
+
+
+
+
+
+
+
+
+### Add or Update New Property in Metadata
+
+To add or update a property in the `metadata`, pass the property in the request body as a key-value pair. This won't affect existing properties in the metadata.
+
+
+
+
+
+
+
+
+
+```js title="Add new metadata property"
+sdk.admin.product.update("prod_123", {
+ metadata: {
+ new_property: "value"
+ }
+})
+```
+
+
+
+
+
+```json title="Result"
+{
+ "id": "prod_123",
+ "metadata": {
+ "new_property": "value",
+ "old_property": "value"
+ }
+}
+```
+
+
+
+
+
+
+
+
+
+
+
+
+
+### Update Nested Objects in Metadata
+
+When updating a nested object in the `metadata`, you must pass the entire object in the request body.
+
+Medusa doesn't merge nested objects, so if you pass a partial object, the existing properties in the nested object will be removed.
+
+
+
+
+
+
+
+
+
+```js title="Update nested object in metadata"
+sdk.admin.product.update("prod_123", {
+ metadata: {
+ nested_object: {
+ property1: "value1",
+ property2: "value2"
+ }
+ }
+})
+```
+
+
+
+
+
+```json title="Result"
+{
+ "id": "prod_123",
+ "metadata": {
+ "nested_object": {
+ "property1": "value1",
+ "property2": "value2"
+ }
+ }
+}
+```
+
+
+
+
+
+
+
+
+
+
+
+
+
+### Remove Property from Metadata
+
+To remove a property from the `metadata`, pass the property in the request body with an empty string value. This will remove the property from the `metadata` without affecting other properties.
+
+
+
+
+
+
+
+
+
+
+
+```js title="Remove metadata property"
+sdk.admin.product.update("prod_123", {
+ metadata: {
+ property_to_remove: ""
+ }
+})
+```
+
+
+
+
+
+```json title="Result"
+{
+ "id": "prod_123",
+ "metadata": {
+ "other_property": "value"
+ }
+}
+```
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
## Select Fields and Relations
diff --git a/www/apps/api-reference/markdown/store.mdx b/www/apps/api-reference/markdown/store.mdx
index 6c5b8f58ab..401a52f0b8 100644
--- a/www/apps/api-reference/markdown/store.mdx
+++ b/www/apps/api-reference/markdown/store.mdx
@@ -441,6 +441,253 @@ x-no-compression: false
+## Manage Metadata
+
+Many data models in Medusa, such as products and carts, have a `metadata` field that allows you to store custom information in key-value pairs.
+
+When setting or updating the `metadata` field using the relevant API routes, Medusa will merge the new metadata with the existing metadata.
+
+
+
+The instructions in this section apply to any [JSON property in a data model](!docs!/learn/fundamentals/data-models/json-properties).
+
+
+
+
+
+
+
+
+
+
+
+### Accepted Values in Metadata
+
+The `metadata` is an object of key-value pairs, where the keys are strings and the values can be one of the following types:
+
+- String
+ - An empty string deletes the property from the metadata.
+- Number
+- Boolean
+- Date
+- Object
+- Arrays of any of the above types
+
+The `metadata` is not validated, so you can store any custom data in it.
+
+
+
+
+
+```ts title="Metadata Example"
+{
+ "metadata": {
+ "category": "electronics",
+ "views": 1500,
+ "is_featured": true,
+ "tags": ["new", "sale"],
+ "details": {
+ "warranty": "2 years",
+ "origin": "USA"
+ }
+ }
+}
+```
+
+
+
+
+
+
+
+
+
+### Add or Update New Property in Metadata
+
+To add or update a property in the `metadata`, pass the property in the request body as a key-value pair. This won't affect existing properties in the metadata.
+
+
+
+
+
+
+
+
+
+```js title="Add new metadata property"
+sdk.store.cart.updateLineItem(
+ "cart_123",
+ "li_123",
+ {
+ metadata: {
+ new_property: "value"
+ }
+ }
+)
+```
+
+
+
+
+
+```json title="Result"
+{
+ "id": "cart_123",
+ "items": [
+ {
+ "id": "li_123",
+ "metadata": {
+ "new_property": "value",
+ "old_property": "value"
+ }
+ }
+ ]
+}
+```
+
+
+
+
+
+
+
+
+
+
+
+
+
+### Update Nested Objects in Metadata
+
+When updating a nested object in the `metadata`, you must pass the entire object in the request body.
+
+Medusa doesn't merge nested objects, so if you pass a partial object, the existing properties in the nested object will be removed.
+
+
+
+
+
+
+
+
+
+```js title="Update nested object in metadata"
+sdk.store.cart.updateLineItem(
+ "cart_123",
+ "li_123",
+ {
+ metadata: {
+ nested_object: {
+ property1: "value1",
+ property2: "value2"
+ }
+ }
+ }
+)
+```
+
+
+
+
+
+```json title="Result"
+{
+ "id": "cart_123",
+ "items": [
+ {
+ "id": "li_123",
+ "metadata": {
+ "nested_object": {
+ "property1": "value1",
+ "property2": "value2"
+ }
+ }
+ }
+ ]
+}
+```
+
+
+
+
+
+
+
+
+
+
+
+
+
+### Remove Property from Metadata
+
+To remove a property from the `metadata`, pass the property in the request body with an empty string value. This will remove the property from the `metadata` without affecting other properties.
+
+
+
+
+
+
+
+
+
+
+
+```js title="Remove metadata property"
+sdk.store.cart.updateLineItem(
+ "cart_123",
+ "li_123",
+ {
+ metadata: {
+ property_to_remove: ""
+ }
+ }
+)
+```
+
+
+
+
+
+```json title="Result"
+{
+ "id": "cart_123",
+ "items": [
+ {
+ "id": "li_123",
+ "metadata": {
+ "other_property": "value"
+ }
+ }
+ ]
+}
+```
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
## Select Fields and Relations
@@ -1167,6 +1414,10 @@ This sorts the products by their `created_at` field in the descending order.
+
+
+
+
@@ -1184,10 +1435,10 @@ Refer to [this guide](!docs!/learn/customization/extend-features/extend-create-p
diff --git a/www/apps/book/app/learn/fundamentals/data-models/json-properties/page.mdx b/www/apps/book/app/learn/fundamentals/data-models/json-properties/page.mdx
new file mode 100644
index 0000000000..0eff132453
--- /dev/null
+++ b/www/apps/book/app/learn/fundamentals/data-models/json-properties/page.mdx
@@ -0,0 +1,237 @@
+export const metadata = {
+ title: `${pageNumber} JSON Properties in Data Models`,
+}
+
+# {metadata.title}
+
+In this chapter, you'll learn how to use and manage [JSON properties](../properties/page.mdx#json) in data models.
+
+## What is a JSON Property?
+
+A JSON property in a data model is a flexible object that can contain various types of values.
+
+For example, you can create a `Brand` data model with a `metadata` JSON property to store additional information about the brand:
+
+```ts highlights={[["6"]]}
+import { model } from "@medusajs/framework/utils"
+
+const Brand = model.define("brand", {
+ id: model.id().primaryKey(),
+ name: model.text(),
+ metadata: model.json(),
+})
+```
+
+### Accepted Values in JSON Property
+
+JSON properties are made up of key-value pairs. The keys are strings, and the values can be one of the following types:
+
+- **Strings**: Text values.
+ - Empty strings remove the property from the JSON object. Learn more in the [Remove a Property from the JSON Property](#remove-a-property-from-the-json-property) section.
+- **Numbers**: Numeric values.
+- **Booleans**: `true` or `false` values.
+- **Nested Objects**: Objects within objects.
+- **Arrays**: Lists of values of any of the above types.
+
+For example, a `metadata` JSON property can look like this:
+
+```json
+{
+ "category": "electronics",
+ "views": 1500,
+ "is_featured": true,
+ "tags": ["new", "sale"],
+ "details": {
+ "warranty": "2 years",
+ "origin": "USA"
+ }
+}
+```
+
+### What are JSON Properties Useful For?
+
+JSON properties allow you to store flexible and dynamic data structures that can evolve over time without requiring changes to the database schema.
+
+Most data models in Medusa's Commerce Modules have a `metadata` property that is a JSON object. `metadata` allows you to store custom information that is not part of the core data model.
+
+Some examples of data to store in JSON properties:
+
+- Custom gift message for line items in an order.
+- Product's ID in a third-party system.
+- Brand's category or tags.
+
+### What are JSON Properties Not Useful For?
+
+JSON properties are not suitable for structured data that requires strict validation or relationships with other data models.
+
+For example, if you want to re-use brands across different products, it's better to create a `Brand` data model and [define a link](../../module-links/page.mdx) to the `Product` data model instead of storing brand information in the product's `metadata` JSON property.
+
+---
+
+## How to Manage JSON Properties?
+
+### How Medusa Updates JSON Properties
+
+Consider a Brand Module with a `Brand` data model as shown [in the previous section](#what-are-json-properties). The [module's service](../../modules/page.mdx#2-create-service) will extend `MedusaService`, which generates methods like [updateBrands](!resources!/service-factory-reference/methods/update) to update a brand.
+
+When you pass a JSON property in the `updateBrands` method, Medusa will merge the provided JSON object with the existing one in the database. So, only the properties you pass will be updated, and the rest will remain unchanged.
+
+The following sections show examples of how to add, update, and remove properties in a JSON property.
+
+
+
+The following examples assume you have a `brandModuleService` that is resolved from the [Medusa container](../../medusa-container/page.mdx). For example:
+
+```ts
+const brandModuleService = container.resolve(BRAND_MODULE)
+```
+
+
+
+### Add a Property to the JSON Property
+
+Continuing with the Brand example, to add a `category` property to the `metadata` property, pass the new property in the `update` or `create` methods:
+
+```ts
+const brand = await brandModuleService.updateBrands({
+ id: "brand_123",
+ metadata: {
+ category: "electronics",
+ }
+})
+```
+
+The brand record will now have the `metadata` property updated to include the new `category` property:
+
+```json title="Result"
+{
+ "id": "brand_123",
+ "metadata": {
+ "category": "electronics"
+ }
+}
+```
+
+If you want to add another `is_featured` property later, you can do so by passing it in the update method again without affecting the existing properties:
+
+```ts
+const brand = await brandModuleService.updateBrands({
+ id: "brand_123",
+ metadata: {
+ is_featured: true,
+ }
+})
+```
+
+The brand record will now have the `metadata` property updated to include both `category` and `is_featured` properties:
+
+```json title="Result"
+{
+ "id": "brand_123",
+ "metadata": {
+ "category": "electronics",
+ "is_featured": true
+ }
+}
+```
+
+### Update an Existing Property in the JSON Property
+
+To update an existing property in the JSON property, pass the updated value in the `update` method.
+
+Continuing with the Brand example, to update the `category` property in the `metadata`:
+
+```ts
+const brand = await brandModuleService.updateBrands({
+ id: "brand_123",
+ metadata: {
+ category: "home appliances",
+ }
+})
+```
+
+The brand record will now have the `metadata` property updated to reflect the new `category` value, and existing properties will remain unchanged:
+
+```json title="Result"
+{
+ "id": "brand_123",
+ "metadata": {
+ "category": "home appliances",
+ "is_featured": true
+ }
+}
+```
+
+### Caveat: Updating Nested Objects
+
+If you want to update a nested object within the JSON property, you need to provide the entire nested object in the update method.
+
+For example, consider that you have a `details` object within the `metadata`:
+
+```json
+{
+ "id": "brand_123",
+ "metadata": {
+ "category": "electronics",
+ "details": {
+ "warranty": "1 year",
+ "origin": "China"
+ }
+ }
+}
+```
+
+To update the `warranty` property within the `details` object, you need to provide the entire `details` object in the update method:
+
+```ts
+const brand = await brandModuleService.updateBrands({
+ id: "brand_123",
+ metadata: {
+ details: {
+ warranty: "2 years",
+ origin: "China"
+ }
+ }
+})
+```
+
+The brand record will now have the `metadata` property updated with the new `warranty` value:
+
+```json title="Result"
+{
+ "id": "brand_123",
+ "metadata": {
+ "category": "electronics",
+ "details": {
+ "warranty": "2 years",
+ "origin": "China"
+ }
+ }
+}
+```
+
+### Remove a Property from the JSON Property
+
+To remove a property from the JSON property, you can pass an empty string as the value of the property in the update method.
+
+For example, to remove the `is_featured` property from the `metadata`:
+
+```ts
+const brand = await brandModuleService.updateBrands({
+ id: "brand_123",
+ metadata: {
+ is_featured: "",
+ }
+})
+```
+
+The brand record will now have the `metadata` property updated to remove the `is_featured` property:
+
+```json title="Result"
+{
+ "id": "brand_123",
+ "metadata": {
+ "category": "home appliances"
+ }
+}
+```
diff --git a/www/apps/book/app/learn/fundamentals/data-models/properties/page.mdx b/www/apps/book/app/learn/fundamentals/data-models/properties/page.mdx
index fd0bfec50a..1ea9ef7170 100644
--- a/www/apps/book/app/learn/fundamentals/data-models/properties/page.mdx
+++ b/www/apps/book/app/learn/fundamentals/data-models/properties/page.mdx
@@ -220,7 +220,7 @@ export default Post
### json
-The `json` method defines a property whose value is a stringified JSON object.
+The `json` method defines a property whose value is stored as a stringified JSON object in the database.
For example:
@@ -237,6 +237,8 @@ const Post = model.define("post", {
export default Post
```
+Learn more in the [JSON Properties](../json-properties/page.mdx) chapter.
+
### array
The `array` method defines an array of strings property.
diff --git a/www/apps/book/generated/edit-dates.mjs b/www/apps/book/generated/edit-dates.mjs
index e1b2d470e2..63edcd9062 100644
--- a/www/apps/book/generated/edit-dates.mjs
+++ b/www/apps/book/generated/edit-dates.mjs
@@ -115,7 +115,7 @@ export const generatedEditDates = {
"app/learn/configurations/ts-aliases/page.mdx": "2025-07-23T15:32:18.008Z",
"app/learn/production/worker-mode/page.mdx": "2025-07-18T15:19:45.352Z",
"app/learn/fundamentals/module-links/read-only/page.mdx": "2025-07-25T07:58:54.327Z",
- "app/learn/fundamentals/data-models/properties/page.mdx": "2025-07-25T13:40:28.866Z",
+ "app/learn/fundamentals/data-models/properties/page.mdx": "2025-07-31T08:22:20.431Z",
"app/learn/fundamentals/framework/page.mdx": "2025-06-26T14:26:22.120Z",
"app/learn/fundamentals/api-routes/retrieve-custom-links/page.mdx": "2025-07-14T10:24:32.582Z",
"app/learn/fundamentals/workflows/errors/page.mdx": "2025-04-25T14:26:25.000Z",
@@ -126,5 +126,6 @@ export const generatedEditDates = {
"app/learn/installation/docker/page.mdx": "2025-07-23T15:34:18.530Z",
"app/learn/fundamentals/generated-types/page.mdx": "2025-07-25T13:17:35.319Z",
"app/learn/introduction/from-v1-to-v2/page.mdx": "2025-07-30T08:13:48.592Z",
- "app/learn/debugging-and-testing/debug-workflows/page.mdx": "2025-07-30T13:45:14.117Z"
+ "app/learn/debugging-and-testing/debug-workflows/page.mdx": "2025-07-30T13:45:14.117Z",
+ "app/learn/fundamentals/data-models/json-properties/page.mdx": "2025-07-31T08:38:55.014Z"
}
\ No newline at end of file
diff --git a/www/apps/book/generated/sidebar.mjs b/www/apps/book/generated/sidebar.mjs
index 5c2972e06c..0eea209e41 100644
--- a/www/apps/book/generated/sidebar.mjs
+++ b/www/apps/book/generated/sidebar.mjs
@@ -513,6 +513,16 @@ export const generatedSidebars = [
"chapterTitle": "3.5.2. Properties",
"number": "3.5.2."
},
+ {
+ "loaded": true,
+ "isPathHref": true,
+ "type": "link",
+ "path": "/learn/fundamentals/data-models/json-properties",
+ "title": "JSON Properties",
+ "children": [],
+ "chapterTitle": "3.5.3. JSON Properties",
+ "number": "3.5.3."
+ },
{
"loaded": true,
"isPathHref": true,
@@ -520,8 +530,8 @@ export const generatedSidebars = [
"path": "/learn/fundamentals/data-models/relationships",
"title": "Relationships",
"children": [],
- "chapterTitle": "3.5.3. Relationships",
- "number": "3.5.3."
+ "chapterTitle": "3.5.4. Relationships",
+ "number": "3.5.4."
},
{
"loaded": true,
@@ -530,8 +540,8 @@ export const generatedSidebars = [
"path": "/learn/fundamentals/data-models/manage-relationships",
"title": "Manage Relationships",
"children": [],
- "chapterTitle": "3.5.4. Manage Relationships",
- "number": "3.5.4."
+ "chapterTitle": "3.5.5. Manage Relationships",
+ "number": "3.5.5."
},
{
"loaded": true,
@@ -540,8 +550,8 @@ export const generatedSidebars = [
"path": "/learn/fundamentals/data-models/index",
"title": "Define Index",
"children": [],
- "chapterTitle": "3.5.5. Define Index",
- "number": "3.5.5."
+ "chapterTitle": "3.5.6. Define Index",
+ "number": "3.5.6."
},
{
"loaded": true,
@@ -550,8 +560,8 @@ export const generatedSidebars = [
"path": "/learn/fundamentals/data-models/check-constraints",
"title": "Check Constraints",
"children": [],
- "chapterTitle": "3.5.6. Check Constraints",
- "number": "3.5.6."
+ "chapterTitle": "3.5.7. Check Constraints",
+ "number": "3.5.7."
},
{
"loaded": true,
@@ -560,8 +570,8 @@ export const generatedSidebars = [
"path": "/learn/fundamentals/data-models/write-migration",
"title": "Migrations",
"children": [],
- "chapterTitle": "3.5.7. Migrations",
- "number": "3.5.7."
+ "chapterTitle": "3.5.8. Migrations",
+ "number": "3.5.8."
}
],
"chapterTitle": "3.5. Data Models",
diff --git a/www/apps/book/public/llms-full.txt b/www/apps/book/public/llms-full.txt
index 9e41bcd17a..5922c8aac4 100644
--- a/www/apps/book/public/llms-full.txt
+++ b/www/apps/book/public/llms-full.txt
@@ -9604,6 +9604,237 @@ class BlogModuleService extends MedusaService({ Post }) {
```
+# JSON Properties in Data Models
+
+In this chapter, you'll learn how to use and manage [JSON properties](https://docs.medusajs.com/learn/fundamentals/data-models/properties#json/index.html.md) in data models.
+
+## What is a JSON Property?
+
+A JSON property in a data model is a flexible object that can contain various types of values.
+
+For example, you can create a `Brand` data model with a `metadata` JSON property to store additional information about the brand:
+
+```ts highlights={[["6"]]}
+import { model } from "@medusajs/framework/utils"
+
+const Brand = model.define("brand", {
+ id: model.id().primaryKey(),
+ name: model.text(),
+ metadata: model.json(),
+})
+```
+
+### Accepted Values in JSON Property
+
+JSON properties are made up of key-value pairs. The keys are strings, and the values can be one of the following types:
+
+- **Strings**: Text values.
+ - Empty strings remove the property from the JSON object. Learn more in the [Remove a Property from the JSON Property](#remove-a-property-from-the-json-property) section.
+- **Numbers**: Numeric values.
+- **Booleans**: `true` or `false` values.
+- **Nested Objects**: Objects within objects.
+- **Arrays**: Lists of values of any of the above types.
+
+For example, a `metadata` JSON property can look like this:
+
+```json
+{
+ "category": "electronics",
+ "views": 1500,
+ "is_featured": true,
+ "tags": ["new", "sale"],
+ "details": {
+ "warranty": "2 years",
+ "origin": "USA"
+ }
+}
+```
+
+### What are JSON Properties Useful For?
+
+JSON properties allow you to store flexible and dynamic data structures that can evolve over time without requiring changes to the database schema.
+
+Most data models in Medusa's Commerce Modules have a `metadata` property that is a JSON object. `metadata` allows you to store custom information that is not part of the core data model.
+
+Some examples of data to store in JSON properties:
+
+- Custom gift message for line items in an order.
+- Product's ID in a third-party system.
+- Brand's category or tags.
+
+### What are JSON Properties Not Useful For?
+
+JSON properties are not suitable for structured data that requires strict validation or relationships with other data models.
+
+For example, if you want to re-use brands across different products, it's better to create a `Brand` data model and [define a link](https://docs.medusajs.com/learn/fundamentals/module-links/index.html.md) to the `Product` data model instead of storing brand information in the product's `metadata` JSON property.
+
+***
+
+## How to Manage JSON Properties?
+
+### How Medusa Updates JSON Properties
+
+Consider a Brand Module with a `Brand` data model as shown [in the previous section](#what-are-json-properties). The [module's service](https://docs.medusajs.com/learn/fundamentals/modules#2-create-service/index.html.md) will extend `MedusaService`, which generates methods like [updateBrands](https://docs.medusajs.com/resources/service-factory-reference/methods/update/index.html.md) to update a brand.
+
+When you pass a JSON property in the `updateBrands` method, Medusa will merge the provided JSON object with the existing one in the database. So, only the properties you pass will be updated, and the rest will remain unchanged.
+
+The following sections show examples of how to add, update, and remove properties in a JSON property.
+
+The following examples assume you have a `brandModuleService` that is resolved from the [Medusa container](https://docs.medusajs.com/learn/fundamentals/medusa-container/index.html.md). For example:
+
+```ts
+const brandModuleService = container.resolve(BRAND_MODULE)
+```
+
+### Add a Property to the JSON Property
+
+Continuing with the Brand example, to add a `category` property to the `metadata` property, pass the new property in the `update` or `create` methods:
+
+```ts
+const brand = await brandModuleService.updateBrands({
+ id: "brand_123",
+ metadata: {
+ category: "electronics",
+ }
+})
+```
+
+The brand record will now have the `metadata` property updated to include the new `category` property:
+
+```json title="Result"
+{
+ "id": "brand_123",
+ "metadata": {
+ "category": "electronics"
+ }
+}
+```
+
+If you want to add another `is_featured` property later, you can do so by passing it in the update method again without affecting the existing properties:
+
+```ts
+const brand = await brandModuleService.updateBrands({
+ id: "brand_123",
+ metadata: {
+ is_featured: true,
+ }
+})
+```
+
+The brand record will now have the `metadata` property updated to include both `category` and `is_featured` properties:
+
+```json title="Result"
+{
+ "id": "brand_123",
+ "metadata": {
+ "category": "electronics",
+ "is_featured": true
+ }
+}
+```
+
+### Update an Existing Property in the JSON Property
+
+To update an existing property in the JSON property, pass the updated value in the `update` method.
+
+Continuing with the Brand example, to update the `category` property in the `metadata`:
+
+```ts
+const brand = await brandModuleService.updateBrands({
+ id: "brand_123",
+ metadata: {
+ category: "home appliances",
+ }
+})
+```
+
+The brand record will now have the `metadata` property updated to reflect the new `category` value, and existing properties will remain unchanged:
+
+```json title="Result"
+{
+ "id": "brand_123",
+ "metadata": {
+ "category": "home appliances",
+ "is_featured": true
+ }
+}
+```
+
+### Caveat: Updating Nested Objects
+
+If you want to update a nested object within the JSON property, you need to provide the entire nested object in the update method.
+
+For example, consider that you have a `details` object within the `metadata`:
+
+```json
+{
+ "id": "brand_123",
+ "metadata": {
+ "category": "electronics",
+ "details": {
+ "warranty": "1 year",
+ "origin": "China"
+ }
+ }
+}
+```
+
+To update the `warranty` property within the `details` object, you need to provide the entire `details` object in the update method:
+
+```ts
+const brand = await brandModuleService.updateBrands({
+ id: "brand_123",
+ metadata: {
+ details: {
+ warranty: "2 years",
+ origin: "China"
+ }
+ }
+})
+```
+
+The brand record will now have the `metadata` property updated with the new `warranty` value:
+
+```json title="Result"
+{
+ "id": "brand_123",
+ "metadata": {
+ "category": "electronics",
+ "details": {
+ "warranty": "2 years",
+ "origin": "China"
+ }
+ }
+}
+```
+
+### Remove a Property from the JSON Property
+
+To remove a property from the JSON property, you can pass an empty string as the value of the property in the update method.
+
+For example, to remove the `is_featured` property from the `metadata`:
+
+```ts
+const brand = await brandModuleService.updateBrands({
+ id: "brand_123",
+ metadata: {
+ is_featured: "",
+ }
+})
+```
+
+The brand record will now have the `metadata` property updated to remove the `is_featured` property:
+
+```json title="Result"
+{
+ "id": "brand_123",
+ "metadata": {
+ "category": "home appliances"
+ }
+}
+```
+
+
# Manage Relationships
In this chapter, you'll learn how to manage relationships between data models when creating, updating, or retrieving records using the module's main service.
@@ -10114,7 +10345,7 @@ export default Post
### json
-The `json` method defines a property whose value is a stringified JSON object.
+The `json` method defines a property whose value is stored as a stringified JSON object in the database.
For example:
@@ -10129,6 +10360,8 @@ const Post = model.define("post", {
export default Post
```
+Learn more in the [JSON Properties](https://docs.medusajs.com/learn/fundamentals/data-models/json-properties/index.html.md) chapter.
+
### array
The `array` method defines an array of strings property.
@@ -80661,6 +80894,40 @@ Learn more about accepted filters in [this documentation](https://docs.medusajs.
The method returns an array of objects of updated records.
+***
+
+## Update a JSON Property
+
+```ts
+const post = await postModuleService.updatePosts({
+ id: "123",
+ name: "My Post",
+ metadata: {
+ category: "news",
+ tags: ["update", "json"],
+ },
+})
+```
+
+When you have a JSON property in your data model, you can update it by adding, updating, or removing properties within that JSON object. Medusa will merge the properties you pass in the `update` method with the existing JSON object.
+
+### Remove a Property from the JSON Property
+
+```ts
+const post = await postModuleService.updatePosts({
+ id: "123",
+ metadata: {
+ is_featured: "",
+ },
+})
+```
+
+To remove a property from the JSON object, you can set its value to an empty string.
+
+### Learn More about Updating JSON Properties
+
+Refer to the [JSON Properties](https://docs.medusajs.com/docs/learn/fundamentals/data-models/json-properties/index.html.md) documentation to learn more about how JSON properties work in Medusa and how to update them.
+
# Service Factory Reference
diff --git a/www/apps/book/sidebar.mjs b/www/apps/book/sidebar.mjs
index f7d86b56ba..81c8c90cc0 100644
--- a/www/apps/book/sidebar.mjs
+++ b/www/apps/book/sidebar.mjs
@@ -274,6 +274,11 @@ export const sidebars = [
path: "/learn/fundamentals/data-models/properties",
title: "Properties",
},
+ {
+ type: "link",
+ path: "/learn/fundamentals/data-models/json-properties",
+ title: "JSON Properties",
+ },
{
type: "link",
path: "/learn/fundamentals/data-models/relationships",
diff --git a/www/apps/resources/app/service-factory-reference/methods/update/page.mdx b/www/apps/resources/app/service-factory-reference/methods/update/page.mdx
index 1d6305043d..18b85de1e7 100644
--- a/www/apps/resources/app/service-factory-reference/methods/update/page.mdx
+++ b/www/apps/resources/app/service-factory-reference/methods/update/page.mdx
@@ -135,3 +135,37 @@ Learn more about accepted filters in [this documentation](../../tips/filtering/p
### Returns
The method returns an array of objects of updated records.
+
+---
+
+## Update a JSON Property
+
+```ts
+const post = await postModuleService.updatePosts({
+ id: "123",
+ name: "My Post",
+ metadata: {
+ category: "news",
+ tags: ["update", "json"],
+ },
+})
+```
+
+When you have a JSON property in your data model, you can update it by adding, updating, or removing properties within that JSON object. Medusa will merge the properties you pass in the `update` method with the existing JSON object.
+
+### Remove a Property from the JSON Property
+
+```ts
+const post = await postModuleService.updatePosts({
+ id: "123",
+ metadata: {
+ is_featured: "",
+ },
+})
+```
+
+To remove a property from the JSON object, you can set its value to an empty string.
+
+### Learn More about Updating JSON Properties
+
+Refer to the [JSON Properties](!docs!/learn/fundamentals/data-models/json-properties) documentation to learn more about how JSON properties work in Medusa and how to update them.
diff --git a/www/apps/resources/generated/edit-dates.mjs b/www/apps/resources/generated/edit-dates.mjs
index 8d5d360177..deb490ee4c 100644
--- a/www/apps/resources/generated/edit-dates.mjs
+++ b/www/apps/resources/generated/edit-dates.mjs
@@ -131,7 +131,7 @@ export const generatedEditDates = {
"app/service-factory-reference/methods/restore/page.mdx": "2024-07-31T17:01:33+03:00",
"app/service-factory-reference/methods/retrieve/page.mdx": "2024-07-31T17:01:33+03:00",
"app/service-factory-reference/methods/soft-delete/page.mdx": "2024-07-31T17:01:33+03:00",
- "app/service-factory-reference/methods/update/page.mdx": "2024-07-31T17:01:33+03:00",
+ "app/service-factory-reference/methods/update/page.mdx": "2025-07-31T08:24:03.685Z",
"app/service-factory-reference/tips/filtering/page.mdx": "2025-04-23T14:38:29.068Z",
"app/service-factory-reference/page.mdx": "2024-07-26T14:40:56+00:00",
"app/storefront-development/cart/context/page.mdx": "2025-03-27T14:47:14.258Z",
diff --git a/www/apps/resources/generated/generated-commerce-modules-sidebar.mjs b/www/apps/resources/generated/generated-commerce-modules-sidebar.mjs
index 53a9074089..584d34fa0c 100644
--- a/www/apps/resources/generated/generated-commerce-modules-sidebar.mjs
+++ b/www/apps/resources/generated/generated-commerce-modules-sidebar.mjs
@@ -6102,6 +6102,14 @@ const generatedgeneratedCommerceModulesSidebarSidebar = {
"path": "https://docs.medusajs.com/resources/how-to-tutorials/tutorials/gift-message",
"children": []
},
+ {
+ "loaded": true,
+ "isPathHref": true,
+ "type": "ref",
+ "title": "Generate Invoices",
+ "path": "https://docs.medusajs.com/resources/how-to-tutorials/tutorials/invoice-generator",
+ "children": []
+ },
{
"loaded": true,
"isPathHref": true,
diff --git a/www/apps/resources/generated/generated-tools-sidebar.mjs b/www/apps/resources/generated/generated-tools-sidebar.mjs
index 600c14d321..d912a536b9 100644
--- a/www/apps/resources/generated/generated-tools-sidebar.mjs
+++ b/www/apps/resources/generated/generated-tools-sidebar.mjs
@@ -819,6 +819,14 @@ const generatedgeneratedToolsSidebarSidebar = {
"path": "https://docs.medusajs.com/resources/how-to-tutorials/tutorials/first-purchase-discounts",
"children": []
},
+ {
+ "loaded": true,
+ "isPathHref": true,
+ "type": "ref",
+ "title": "Generate Invoices",
+ "path": "https://docs.medusajs.com/resources/how-to-tutorials/tutorials/invoice-generator",
+ "children": []
+ },
{
"loaded": true,
"isPathHref": true,