3.4.7. Query Context

In this chapter, you'll learn how to pass context when retrieving data with Query.

What is Query Context?#

Query context is additional information you pass to Query when retrieving data. You can use it to apply custom transformations to the returned data based on the current context.

For example, consider a Blog Module with posts and authors. You can send the user's language as context to Query to retrieve posts in the user's language.

Medusa also uses Query Context to retrieve product variants' prices based on the customer's currency.


How to Pass Query Context#

The query.graph method accepts an optional context parameter. You can use this to pass additional context to the data model you're retrieving (for example, post) or its related and linked models (for example, author).

You can 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 context:

Code
1import { QueryContext } from "@medusajs/framework/utils"2
3// ...4
5const { data } = await query.graph({6  entity: "post",7  fields: ["*"],8  context: QueryContext({9    lang: "es",10  }),11})

In this example, you pass a lang property with the value es in the context. You create the context using QueryContext.

How to Handle Query Context#

To handle the Query context passed while retrieving records of your data models, override the generated list method of the associated module's service.

For example, continuing the example above, you can override the listPosts method of the Blog Module's service to handle the Query context:

Code
1import { MedusaContext, MedusaService } from "@medusajs/framework/utils"2import { Context, FindConfig } from "@medusajs/framework/types"3import Post from "./models/post"4import Author from "./models/author"5
6class BlogModuleService extends MedusaService({7  Post,8  Author,9}){10  // @ts-ignore11  async listPosts(12    filters?: any, 13    config?: FindConfig<any> | undefined, 14    @MedusaContext() sharedContext?: Context | undefined15  ) {16    const context = filters.context ?? {}17    delete filters.context18
19    let posts = await super.listPosts(filters, config, sharedContext)20
21    if (context.lang === "es") {22      posts = posts.map((post) => {23        return {24          ...post,25          title: post.title + " en español",26        }27      })28    }29
30    return posts31  }32}33
34export default BlogModuleService

In the above example, you override the generated listPosts method. This method receives the filters passed to query.graph as its first parameter. The first parameter includes a context property that holds the Query context alsopassed to query.graph.

You extract the context from filters, then retrieve the posts using the parent's listPosts method. If the language is set in the context, you transform the post titles.

All posts returned will now have their titles appended with "en español".

Using Pagination with Query#

If you pass pagination fields to query.graph, you must also override the generated listAndCount method in the service.

For example, following the previous example, you must override the listAndCountPosts method of the Blog Module's service:

Code
1import { MedusaContext, MedusaService } from "@medusajs/framework/utils"2import { Context, FindConfig } from "@medusajs/framework/types"3import Post from "./models/post"4import Author from "./models/author"5
6class BlogModuleService extends MedusaService({7  Post,8  Author,9}){10  // @ts-ignore11  async listAndCountPosts(12    filters?: any, 13    config?: FindConfig<any> | undefined, 14    @MedusaContext() sharedContext?: Context | undefined15  ) {16    const context = filters.context ?? {}17    delete filters.context18
19    const result = await super.listAndCountPosts(20      filters, 21      config, 22      sharedContext23    )24
25    if (context.lang === "es") {26      result[0] = result[0].map((post) => {27        return {28          ...post,29          title: post.title + " en español",30        }31      })32    }33
34    return result35  }36}37
38export default BlogModuleService

Now, the listAndCountPosts method will handle the context passed to query.graph when you pass pagination fields. You can also move the logic to transform the post titles to a separate method and call it from both listPosts and listAndCountPosts.


If you're retrieving a data model and want to pass Query context to its associated model in the same module, pass them as part of QueryContext's parameter. Then, you can handle them in the same list method.

Note: To pass Query context to linked data models, check out the next section.

For example, to pass a context for the post's authors:

Code
1const { data } = await query.graph({2  entity: "post",3  fields: ["*"],4  context: QueryContext({5    lang: "es",6    author: QueryContext({7      lang: "es",8    }),9  }),10})

Then, in the listPosts method, you can handle the context for the post's authors:

Code
1import { MedusaContext, MedusaService } from "@medusajs/framework/utils"2import { Context, FindConfig } from "@medusajs/framework/types"3import Post from "./models/post"4import Author from "./models/author"5
6class BlogModuleService extends MedusaService({7  Post,8  Author,9}){10  // @ts-ignore11  async listPosts(12    filters?: any, 13    config?: FindConfig<any> | undefined, 14    @MedusaContext() sharedContext?: Context | undefined15  ) {16    const context = filters.context ?? {}17    delete filters.context18
19    let posts = await super.listPosts(filters, config, sharedContext)20
21    const isPostLangEs = context.lang === "es"22    const isAuthorLangEs = context.author?.lang === "es"23
24    if (isPostLangEs || isAuthorLangEs) {25      posts = posts.map((post) => {26        return {27          ...post,28          title: isPostLangEs ? post.title + " en español" : post.title,29          author: {30            ...post.author,31            name: isAuthorLangEs ? post.author.name + " en español" : post.author.name,32          },33        }34      })35    }36
37    return posts38  }39}40
41export default BlogModuleService

The context in filters will also include the context for author, which you can use to transform the post's authors.


Passing Query Context to Linked Data Models#

If you're retrieving a data model and want to pass Query context to a linked model in a different module, pass an object to the context property instead. The object's keys should be the linked model's name, and the values should be the Query 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:

Code
1const { data } = await query.graph({2  entity: "product",3  fields: ["*", "post.*"],4  context: {5    post: QueryContext({6      lang: "es",7    }),8  },9})

In this example, you retrieve products and their associated posts. You also pass Query context for post, indicating the customer's language.

To handle the context, override the generated listPosts method of the Blog Module as explained previously.

Was this chapter helpful?
Ask Anything
FAQ
What is Medusa?
How can I create a module?
How can I create a data model?
How do I create a workflow?
How can I extend a data model in the Product Module?
Recipes
How do I build a marketplace with Medusa?
How do I build digital products with Medusa?
How do I build subscription-based purchases with Medusa?
What other recipes are available in the Medusa documentation?
Chat is cleared on refresh
Line break