|
| 1 | +export const metadata = { |
| 2 | + title: `${pageNumber} Query Context`, |
| 3 | +} |
| 4 | + |
| 5 | +# {metadata.title} |
| 6 | + |
| 7 | +In this chapter, you'll learn how to pass contexts when retrieving data with [Query](../query/page.mdx). |
| 8 | + |
| 9 | +## What is Query Context? |
| 10 | + |
| 11 | +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. |
| 12 | + |
| 13 | +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). |
| 14 | + |
| 15 | +--- |
| 16 | + |
| 17 | +## How to Use Query Context |
| 18 | + |
| 19 | +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`). |
| 20 | + |
| 21 | +You initialize a context using `QueryContext` from the Modules SDK. It accepts an object of contexts as an argument. |
| 22 | + |
| 23 | +For example, to retrieve posts using Query while passing the user's language as a context: |
| 24 | + |
| 25 | +export const highlights1 = [ |
| 26 | + ["4", "context", "Pass additional context to the query."], |
| 27 | + ["4", "QueryContext", "Create a query context."] |
| 28 | +] |
| 29 | + |
| 30 | +```ts |
| 31 | +const { data } = await query.graph({ |
| 32 | + entity: "post", |
| 33 | + fields: ["*"], |
| 34 | + context: QueryContext({ |
| 35 | + lang: "es", |
| 36 | + }) |
| 37 | +}) |
| 38 | +``` |
| 39 | + |
| 40 | +In this example, you pass in the context a `lang` property whose value is `es`. |
| 41 | + |
| 42 | +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. |
| 43 | + |
| 44 | +For example, continuing the example above, you can override the `listPosts` method of the Blog Module's service to handle the context: |
| 45 | + |
| 46 | +export const highlights2 = [ |
| 47 | + ["11", "listPosts", "Override the generated listPosts method."], |
| 48 | + ["16", "context", "The context is passed as part of `filters`."], |
| 49 | + ["19", "super", "Use the parent's `listPosts` method to retrieve the posts."], |
| 50 | + ["21", "", "If the language is set in the context, you transform the titles of the posts."] |
| 51 | +] |
| 52 | + |
| 53 | +```ts highlights={highlights2} |
| 54 | +import { MedusaContext, MedusaService } from "@medusajs/framework/utils" |
| 55 | +import { Context, FindConfig } from "@medusajs/framework/types" |
| 56 | +import Post from "./models/post" |
| 57 | +import Author from "./models/author" |
| 58 | + |
| 59 | +class BlogModuleService extends MedusaService({ |
| 60 | + Post, |
| 61 | + Author |
| 62 | +}){ |
| 63 | + // @ts-ignore |
| 64 | + async listPosts( |
| 65 | + filters?: any, |
| 66 | + config?: FindConfig<any> | undefined, |
| 67 | + @MedusaContext() sharedContext?: Context | undefined |
| 68 | + ) { |
| 69 | + const context = filters.context ?? {} |
| 70 | + delete filters.context |
| 71 | + |
| 72 | + let posts = await super.listPosts(filters, config, sharedContext) |
| 73 | + |
| 74 | + if (context.lang === "es") { |
| 75 | + posts = posts.map((post) => { |
| 76 | + return { |
| 77 | + ...post, |
| 78 | + title: post.title + " en español", |
| 79 | + } |
| 80 | + }) |
| 81 | + } |
| 82 | + |
| 83 | + return posts |
| 84 | + } |
| 85 | +} |
| 86 | + |
| 87 | +export default BlogModuleService |
| 88 | +``` |
| 89 | + |
| 90 | +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. |
| 91 | + |
| 92 | +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. |
| 93 | + |
| 94 | +All posts returned will now have their titles appended with "en español". |
| 95 | + |
| 96 | +<Note title="Tip"> |
| 97 | + |
| 98 | +Learn more about the generated `list` method in [this reference](!resources!/service-factory-reference/methods/list). |
| 99 | + |
| 100 | +</Note> |
| 101 | + |
| 102 | +--- |
| 103 | + |
| 104 | +## Passing Query Context to Related Data Models |
| 105 | + |
| 106 | +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. |
| 107 | + |
| 108 | +<Note> |
| 109 | + |
| 110 | +For linked data models, check out the [next section](#passing-query-context-to-linked-data-models). |
| 111 | + |
| 112 | +</Note> |
| 113 | + |
| 114 | +For example, to pass a context for the post's authors: |
| 115 | + |
| 116 | +export const highlights3 = [ |
| 117 | + ["6", "author", "Pass a context for the author."] |
| 118 | +] |
| 119 | + |
| 120 | +```ts highlights={highlights3} |
| 121 | +const { data } = await query.graph({ |
| 122 | + entity: "post", |
| 123 | + fields: ["*"], |
| 124 | + context: QueryContext({ |
| 125 | + lang: "es", |
| 126 | + author: QueryContext({ |
| 127 | + lang: "es", |
| 128 | + }) |
| 129 | + }) |
| 130 | +}) |
| 131 | +``` |
| 132 | + |
| 133 | +Then, in the `listPosts` method, you can handle the context for the post's authors: |
| 134 | + |
| 135 | +export const highlights4 = [ |
| 136 | + ["22", "context.author?.lang", "Access the author's context."] |
| 137 | +] |
| 138 | + |
| 139 | +```ts highlights={highlights4} |
| 140 | +import { MedusaContext, MedusaService } from "@medusajs/framework/utils" |
| 141 | +import { Context, FindConfig } from "@medusajs/framework/types" |
| 142 | +import Post from "./models/post" |
| 143 | +import Author from "./models/author" |
| 144 | + |
| 145 | +class BlogModuleService extends MedusaService({ |
| 146 | + Post, |
| 147 | + Author |
| 148 | +}){ |
| 149 | + // @ts-ignore |
| 150 | + async listPosts( |
| 151 | + filters?: any, |
| 152 | + config?: FindConfig<any> | undefined, |
| 153 | + @MedusaContext() sharedContext?: Context | undefined |
| 154 | + ) { |
| 155 | + const context = filters.context ?? {} |
| 156 | + delete filters.context |
| 157 | + |
| 158 | + let posts = await super.listPosts(filters, config, sharedContext) |
| 159 | + |
| 160 | + const isPostLangEs = context.lang === "es" |
| 161 | + const isAuthorLangEs = context.author?.lang === "es" |
| 162 | + |
| 163 | + if (isPostLangEs || isAuthorLangEs) { |
| 164 | + posts = posts.map((post) => { |
| 165 | + return { |
| 166 | + ...post, |
| 167 | + title: isPostLangEs ? post.title + " en español" : post.title, |
| 168 | + author: { |
| 169 | + ...post.author, |
| 170 | + name: isAuthorLangEs ? post.author.name + " en español" : post.author.name, |
| 171 | + } |
| 172 | + } |
| 173 | + }) |
| 174 | + } |
| 175 | + |
| 176 | + return posts |
| 177 | + } |
| 178 | +} |
| 179 | + |
| 180 | +export default BlogModuleService |
| 181 | +``` |
| 182 | + |
| 183 | +The context in `filters` will also have the context for `author`, which you can use to make transformations to the post's authors. |
| 184 | + |
| 185 | +--- |
| 186 | + |
| 187 | +## Passing Query Context to Linked Data Models |
| 188 | + |
| 189 | +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. |
| 190 | + |
| 191 | +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: |
| 192 | + |
| 193 | +export const highlights5 = [ |
| 194 | + ["5", "post", "Pass a context for posts."] |
| 195 | +] |
| 196 | + |
| 197 | +```ts highlights={highlights5} |
| 198 | +const { data } = await query.graph({ |
| 199 | + entity: "product", |
| 200 | + fields: ["*", "post.*"], |
| 201 | + context: { |
| 202 | + post: QueryContext({ |
| 203 | + lang: "es", |
| 204 | + }) |
| 205 | + } |
| 206 | +}) |
| 207 | +``` |
| 208 | + |
| 209 | +In this example, you retrieve products and their associated posts. You also pass a context for `post`, indicating the customer's language. |
| 210 | + |
| 211 | +To handle the context, you override the generated `listPosts` method of the Blog Module as explained [previously](#how-to-use-query-context). |
0 commit comments