docs: document Query Context (#11289)

* docs: document Query Context

* add vale exception
This commit is contained in:
Shahed Nasser
2025-02-03 19:43:40 +02:00
committed by GitHub
parent e0851b25ce
commit 94376f6c75
5 changed files with 229 additions and 2 deletions

View File

@@ -0,0 +1,211 @@
export const metadata = {
title: `${pageNumber} Query Context`,
}
# {metadata.title}
In this chapter, you'll learn how to pass contexts when retrieving data with [Query](../query/page.mdx).
## What is Query Context?
Query context is a way to pass additional information when retrieving data with Query. This data can be useful when applying custom transformations to the retrieved data based on the current context.
For example, consider you have a Blog Module with posts and authors. You can accept the user's language as a context and return the posts in the user's language. Another example is how Medusa uses Query Context to [retrieve product variants' prices based on the customer's currency](!resources!/commerce-modules/product/guides/price).
---
## How to Use Query Context
The `query.graph` method accepts an optional `context` parameter that can be used to pass additional context either to the data model you're retrieving (for example, `post`), or its related and linked models (for example, `author`).
You initialize a context using `QueryContext` from the Modules SDK. It accepts an object of contexts as an argument.
For example, to retrieve posts using Query while passing the user's language as a context:
export const highlights1 = [
["4", "context", "Pass additional context to the query."],
["4", "QueryContext", "Create a query context."]
]
```ts
const { data } = await query.graph({
entity: "post",
fields: ["*"],
context: QueryContext({
lang: "es",
})
})
```
In this example, you pass in the context a `lang` property whose value is `es`.
Then, to handle the context while retrieving records of the data model, in the associated module's service you override the generated `list` method of the data model.
For example, continuing the example above, you can override the `listPosts` method of the Blog Module's service to handle the context:
export const highlights2 = [
["11", "listPosts", "Override the generated listPosts method."],
["16", "context", "The context is passed as part of `filters`."],
["19", "super", "Use the parent's `listPosts` method to retrieve the posts."],
["21", "", "If the language is set in the context, you transform the titles of the posts."]
]
```ts highlights={highlights2}
import { MedusaContext, MedusaService } from "@medusajs/framework/utils"
import { Context, FindConfig } from "@medusajs/framework/types"
import Post from "./models/post"
import Author from "./models/author"
class BlogModuleService extends MedusaService({
Post,
Author
}){
// @ts-ignore
async listPosts(
filters?: any,
config?: FindConfig<any> | undefined,
@MedusaContext() sharedContext?: Context | undefined
) {
const context = filters.context ?? {}
delete filters.context
let posts = await super.listPosts(filters, config, sharedContext)
if (context.lang === "es") {
posts = posts.map((post) => {
return {
...post,
title: post.title + " en español",
}
})
}
return posts
}
}
export default BlogModuleService
```
In the above example, you override the generated `listPosts` method. This method receives as a first parameter the filters passed to the query, but it also includes a `context` property that holds the context passed to the query.
You extract the context from `filters`, then retrieve the posts using the parent's `listPosts` method. After that, if the language is set in the context, you transform the titles of the posts.
All posts returned will now have their titles appended with "en español".
<Note title="Tip">
Learn more about the generated `list` method in [this reference](!resources!/service-factory-reference/methods/list).
</Note>
---
## Passing Query Context to Related Data Models
If you're retrieving a data model and you want to pass context to its associated model in the same module, you can pass them as part of `QueryContext`'s parameter, then handle them in the same `list` method.
<Note>
For linked data models, check out the [next section](#passing-query-context-to-linked-data-models).
</Note>
For example, to pass a context for the post's authors:
export const highlights3 = [
["6", "author", "Pass a context for the author."]
]
```ts highlights={highlights3}
const { data } = await query.graph({
entity: "post",
fields: ["*"],
context: QueryContext({
lang: "es",
author: QueryContext({
lang: "es",
})
})
})
```
Then, in the `listPosts` method, you can handle the context for the post's authors:
export const highlights4 = [
["22", "context.author?.lang", "Access the author's context."]
]
```ts highlights={highlights4}
import { MedusaContext, MedusaService } from "@medusajs/framework/utils"
import { Context, FindConfig } from "@medusajs/framework/types"
import Post from "./models/post"
import Author from "./models/author"
class BlogModuleService extends MedusaService({
Post,
Author
}){
// @ts-ignore
async listPosts(
filters?: any,
config?: FindConfig<any> | undefined,
@MedusaContext() sharedContext?: Context | undefined
) {
const context = filters.context ?? {}
delete filters.context
let posts = await super.listPosts(filters, config, sharedContext)
const isPostLangEs = context.lang === "es"
const isAuthorLangEs = context.author?.lang === "es"
if (isPostLangEs || isAuthorLangEs) {
posts = posts.map((post) => {
return {
...post,
title: isPostLangEs ? post.title + " en español" : post.title,
author: {
...post.author,
name: isAuthorLangEs ? post.author.name + " en español" : post.author.name,
}
}
})
}
return posts
}
}
export default BlogModuleService
```
The context in `filters` will also have the context for `author`, which you can use to make transformations to the post's authors.
---
## Passing Query Context to Linked Data Models
If you're retrieving a data model and you want to pass context to a linked model in a different module, pass to the `context` property an object instead, where its keys are the linked model's name and the values are the context for that linked model.
For example, consider the Product Module's `Product` data model is linked to the Blog Module's `Post` data model. You can pass context to the `Post` data model while retrieving products like so:
export const highlights5 = [
["5", "post", "Pass a context for posts."]
]
```ts highlights={highlights5}
const { data } = await query.graph({
entity: "product",
fields: ["*", "post.*"],
context: {
post: QueryContext({
lang: "es",
})
}
})
```
In this example, you retrieve products and their associated posts. You also pass a context for `post`, indicating the customer's language.
To handle the context, you override the generated `listPosts` method of the Blog Module as explained [previously](#how-to-use-query-context).

View File

@@ -114,5 +114,6 @@ export const generatedEditDates = {
"app/learn/fundamentals/plugins/create/page.mdx": "2025-01-31T13:17:48.052Z",
"app/learn/fundamentals/plugins/page.mdx": "2025-01-22T10:14:10.433Z",
"app/learn/customization/reuse-customizations/page.mdx": "2025-01-22T10:01:57.665Z",
"app/learn/update/page.mdx": "2025-01-27T08:45:19.030Z"
"app/learn/update/page.mdx": "2025-01-27T08:45:19.030Z",
"app/learn/fundamentals/module-links/query-context/page.mdx": "2025-02-03T17:04:24.479Z"
}

View File

@@ -368,6 +368,15 @@ export const generatedSidebar = [
"title": "Custom Columns",
"children": [],
"chapterTitle": "3.3.4. Custom Columns"
},
{
"loaded": true,
"isPathHref": true,
"type": "link",
"path": "/learn/fundamentals/module-links/query-context",
"title": "Query Context",
"children": [],
"chapterTitle": "3.3.5. Query Context"
}
],
"chapterTitle": "3.3. Module Links"

View File

@@ -214,6 +214,11 @@ export const sidebar = sidebarAttachHrefCommonOptions([
path: "/learn/fundamentals/module-links/custom-columns",
title: "Custom Columns",
},
{
type: "link",
path: "/learn/fundamentals/module-links/query-context",
title: "Query Context",
},
],
},
{

View File

@@ -17,4 +17,5 @@ exceptions:
- the first module
- the second module
- the module provider
- the specified module
- the specified module
- the associated module