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:
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:
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:
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
.
Passing Query Context to Related Data Models#
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.
For example, to pass a context for the post's authors:
Then, in the listPosts
method, you can handle the context for the post's authors:
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:
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.