docs: add Query documentation (#9079)
- Replace remote query documentation with new Query documentation - Add redirect from old remote query to new query documentation - Update remote query usages across docs to use new query usage.
This commit is contained in:
@@ -17,7 +17,7 @@ export const arrowHighlights = [
|
||||
|
||||
```ts highlights={arrowHighlights}
|
||||
// Don't
|
||||
function ProductWidget () {
|
||||
function ProductWidget() {
|
||||
// ...
|
||||
}
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ const ProductWidget = () => {
|
||||
}
|
||||
|
||||
fetch(`/admin/products`, {
|
||||
credentials: "include"
|
||||
credentials: "include",
|
||||
})
|
||||
.then((res) => res.json())
|
||||
.then(({ count }) => {
|
||||
|
||||
@@ -256,7 +256,7 @@ import {
|
||||
defineMiddlewares,
|
||||
MedusaNextFunction,
|
||||
MedusaRequest,
|
||||
MedusaResponse
|
||||
MedusaResponse,
|
||||
} from "@medusajs/medusa"
|
||||
import { MedusaError } from "@medusajs/utils"
|
||||
|
||||
@@ -268,9 +268,9 @@ export default defineMiddlewares({
|
||||
next: MedusaNextFunction
|
||||
) => {
|
||||
res.status(400).json({
|
||||
error: "Something happened."
|
||||
error: "Something happened.",
|
||||
})
|
||||
}
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
|
||||
@@ -83,7 +83,7 @@ For example:
|
||||
```ts title="src/api/admin/custom/route.ts" highlights={[["15"]]}
|
||||
import type {
|
||||
AuthenticatedMedusaRequest,
|
||||
MedusaResponse
|
||||
MedusaResponse,
|
||||
} from "@medusajs/medusa"
|
||||
|
||||
export const GET = async (
|
||||
|
||||
@@ -17,14 +17,14 @@ export const jsonHighlights = [
|
||||
]
|
||||
|
||||
```ts title="src/api/store/custom/route.ts" highlights={jsonHighlights} apiTesting testApiUrl="http://localhost:9000/store/custom" testApiMethod="GET"
|
||||
import { MedusaRequest, MedusaResponse } from "@medusajs/medusa";
|
||||
import { MedusaRequest, MedusaResponse } from "@medusajs/medusa"
|
||||
|
||||
export const GET = async (
|
||||
req: MedusaRequest,
|
||||
res: MedusaResponse
|
||||
) => {
|
||||
res.json({
|
||||
message: "Hello, World!"
|
||||
message: "Hello, World!",
|
||||
})
|
||||
}
|
||||
```
|
||||
@@ -52,14 +52,14 @@ export const statusHighlight = [
|
||||
]
|
||||
|
||||
```ts title="src/api/store/custom/route.ts" highlights={statusHighlight} apiTesting testApiUrl="http://localhost:9000/store/custom" testApiMethod="GET"
|
||||
import { MedusaRequest, MedusaResponse } from "@medusajs/medusa";
|
||||
import { MedusaRequest, MedusaResponse } from "@medusajs/medusa"
|
||||
|
||||
export const GET = async (
|
||||
req: MedusaRequest,
|
||||
res: MedusaResponse
|
||||
) => {
|
||||
res.status(201).json({
|
||||
message: "Hello, World!"
|
||||
message: "Hello, World!",
|
||||
})
|
||||
}
|
||||
```
|
||||
@@ -84,7 +84,7 @@ export const streamHighlights = [
|
||||
]
|
||||
|
||||
```ts highlights={streamHighlights}
|
||||
import { MedusaRequest, MedusaResponse } from "@medusajs/medusa";
|
||||
import { MedusaRequest, MedusaResponse } from "@medusajs/medusa"
|
||||
|
||||
export const GET = async (
|
||||
req: MedusaRequest,
|
||||
|
||||
@@ -27,7 +27,7 @@ import { z } from "zod"
|
||||
|
||||
export const PostStoreCustomSchema = z.object({
|
||||
a: z.number(),
|
||||
b: z.number()
|
||||
b: z.number(),
|
||||
})
|
||||
```
|
||||
|
||||
@@ -48,7 +48,7 @@ For example, create the file `src/api/middlewares.ts` with the following content
|
||||
```ts title="src/api/middlewares.ts"
|
||||
import { defineMiddlewares } from "@medusajs/medusa"
|
||||
import {
|
||||
validateAndTransformBody
|
||||
validateAndTransformBody,
|
||||
} from "@medusajs/medusa/dist/api/utils/validate-body"
|
||||
import { PostStoreCustomSchema } from "./store/custom/validators"
|
||||
|
||||
@@ -58,7 +58,7 @@ export default defineMiddlewares({
|
||||
matcher: "/store/custom",
|
||||
method: "POST",
|
||||
middlewares: [
|
||||
validateAndTransformBody(PostStoreCustomSchema)
|
||||
validateAndTransformBody(PostStoreCustomSchema),
|
||||
],
|
||||
},
|
||||
],
|
||||
@@ -87,9 +87,9 @@ export const routeHighlights = [
|
||||
]
|
||||
|
||||
```ts title="src/api/store/custom/route.ts" highlights={routeHighlights}
|
||||
import { MedusaRequest, MedusaResponse } from "@medusajs/medusa";
|
||||
import { MedusaRequest, MedusaResponse } from "@medusajs/medusa"
|
||||
import { z } from "zod"
|
||||
import { PostStoreCustomSchema } from "./validators";
|
||||
import { PostStoreCustomSchema } from "./validators"
|
||||
|
||||
type PostStoreCustomSchemaType = z.infer<
|
||||
typeof PostStoreCustomSchema
|
||||
@@ -100,7 +100,7 @@ export const POST = async (
|
||||
res: MedusaResponse
|
||||
) => {
|
||||
res.json({
|
||||
sum: req.validatedBody.a + req.validatedBody.b
|
||||
sum: req.validatedBody.a + req.validatedBody.b,
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
@@ -84,7 +84,7 @@ export const retrieveHighlights = [
|
||||
const product = await helloModuleService.retrieveProduct(
|
||||
"123",
|
||||
{
|
||||
relations: ["orders"]
|
||||
relations: ["orders"],
|
||||
}
|
||||
)
|
||||
```
|
||||
|
||||
@@ -22,10 +22,10 @@ export const highlights = [
|
||||
|
||||
```ts highlights={highlights}
|
||||
import {
|
||||
createWorkflow
|
||||
createWorkflow,
|
||||
} from "@medusajs/workflows-sdk"
|
||||
import {
|
||||
emitEventStep
|
||||
emitEventStep,
|
||||
} from "@medusajs/core-flows"
|
||||
|
||||
const helloWorldWorkflow = createWorkflow(
|
||||
@@ -36,8 +36,8 @@ const helloWorldWorkflow = createWorkflow(
|
||||
emitEventStep({
|
||||
eventName: "custom.created",
|
||||
data: {
|
||||
id: "123"
|
||||
}
|
||||
id: "123",
|
||||
},
|
||||
})
|
||||
}
|
||||
)
|
||||
|
||||
237
www/apps/book/app/advanced-development/modules/query/page.mdx
Normal file
237
www/apps/book/app/advanced-development/modules/query/page.mdx
Normal file
@@ -0,0 +1,237 @@
|
||||
import { TypeList, Tabs, TabsList, TabsTriggerVertical, TabsContent, TabsContentWrapper } from "docs-ui"
|
||||
|
||||
export const metadata = {
|
||||
title: `${pageNumber} Query`,
|
||||
}
|
||||
|
||||
# {metadata.title}
|
||||
|
||||
In this chapter, you’ll learn about the Query utility and how to use it to fetch data from modules.
|
||||
|
||||
<Note>
|
||||
|
||||
Remote Query is now deprecated in favor of Query. Follow this documentation to see the difference in the usage.
|
||||
|
||||
</Note>
|
||||
|
||||
## What is Query?
|
||||
|
||||
Query fetches data across modules. It’s a set of methods registered in the Medusa container under the `query` key.
|
||||
|
||||
In your resources, such as API routes or workflows, you can resolve Query to fetch data across custom modules and Medusa’s commerce modules.
|
||||
|
||||
---
|
||||
|
||||
## Query Example
|
||||
|
||||
For example, create the route `src/api/store/query/route.ts` with the following content:
|
||||
|
||||
export const exampleHighlights = [
|
||||
["13", "", "Resolve Query from the Medusa container."],
|
||||
["15", "graph", "Run a query to retrieve data."],
|
||||
["16", "entryPoint", "The name of the data model you're querying."],
|
||||
["17", "fields", "An array of the data model’s properties to retrieve in the result."],
|
||||
]
|
||||
|
||||
```ts title="src/api/store/query/route.ts" highlights={exampleHighlights} apiTesting testApiMethod="GET" testApiUrl="http://localhost:9000/store/query" collapsibleLines="1-8" expandButtonLabel="Show Imports"
|
||||
import {
|
||||
MedusaRequest,
|
||||
MedusaResponse,
|
||||
} from "@medusajs/medusa"
|
||||
import {
|
||||
ContainerRegistrationKeys,
|
||||
} from "@medusajs/utils"
|
||||
|
||||
export const GET = async (
|
||||
req: MedusaRequest,
|
||||
res: MedusaResponse
|
||||
) => {
|
||||
const query = req.scope.resolve(ContainerRegistrationKeys.QUERY)
|
||||
|
||||
const { data: myCustoms } = await query.graph({
|
||||
entryPoint: "my_custom",
|
||||
fields: ["id", "name"],
|
||||
})
|
||||
|
||||
res.json({ my_customs: myCustoms })
|
||||
}
|
||||
```
|
||||
|
||||
In the above example, you resolve Query from the Medusa container using the `ContainerRegistrationKeys.QUERY` (`query`) key.
|
||||
|
||||
Then, you run a query using its `graph` method. This method accepts as a parameter an object with the following required properties:
|
||||
|
||||
- `entryPoint`: The data model's name, as specified in the first parameter of the `model.define` method used for the data model's definition.
|
||||
- `fields`: An array of the data model’s properties to retrieve in the result.
|
||||
|
||||
The method returns an object that has a `data` property, which holds an array of the retrieved data. For example:
|
||||
|
||||
```json title="Returned Data"
|
||||
{
|
||||
"data": [
|
||||
{
|
||||
"id": "123",
|
||||
"name": "test"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Retrieve Linked Records
|
||||
|
||||
Retrieve the records of a linked data model by passing in `fields` the data model's name suffixed with `.*`.
|
||||
|
||||
For example:
|
||||
|
||||
```ts highlights={[["6"]]}
|
||||
const { data: myCustoms } = await query.graph({
|
||||
entryPoint: "my_custom",
|
||||
fields: [
|
||||
"id",
|
||||
"name",
|
||||
"product.*",
|
||||
],
|
||||
})
|
||||
```
|
||||
|
||||
<Note title="Tip">
|
||||
|
||||
`.*` means that all of data model's properties should be retrieved. To retrieve a specific property, replace the `*` with the property's name. For example, `product.title`.
|
||||
|
||||
</Note>
|
||||
|
||||
### Retrieve List Link Records
|
||||
|
||||
If the linked data model has `isList` enabled in the link definition, pass in `fields` the data model's plural name suffixed with `.*`.
|
||||
|
||||
For example:
|
||||
|
||||
```ts highlights={[["6"]]}
|
||||
const { data: myCustoms } = await query.graph({
|
||||
entryPoint: "my_custom",
|
||||
fields: [
|
||||
"id",
|
||||
"name",
|
||||
"products.*",
|
||||
],
|
||||
})
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Apply Filters
|
||||
|
||||
```ts highlights={[["6"], ["7"], ["8"], ["9"]]}
|
||||
const { data: myCustoms } = await query.graph({
|
||||
entryPoint: "my_custom",
|
||||
fields: ["id", "name"],
|
||||
variables: {
|
||||
filters: {
|
||||
id: [
|
||||
"mc_01HWSVWR4D2XVPQ06DQ8X9K7AX",
|
||||
"mc_01HWSVWK3KYHKQEE6QGS2JC3FX",
|
||||
],
|
||||
},
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
<Note>
|
||||
|
||||
Filters don't apply on fields of linked data models from other modules.
|
||||
|
||||
</Note>
|
||||
|
||||
The `query.graph` function accepts a `variables` property. You can use this property to filter retrieved records.
|
||||
|
||||
<TypeList
|
||||
types={[
|
||||
{
|
||||
name: "variables",
|
||||
type: "`object`",
|
||||
description: "Variables to pass to the query.",
|
||||
children: [
|
||||
{
|
||||
name: "filters",
|
||||
type: "`object`",
|
||||
description: "The filters to apply on any of the data model's properties."
|
||||
}
|
||||
]
|
||||
},
|
||||
]}
|
||||
sectionTitle="Apply Filters"
|
||||
/>
|
||||
|
||||
---
|
||||
|
||||
## Sort Records
|
||||
|
||||
```ts highlights={[["5"], ["6"], ["7"]]}
|
||||
const { data: myCustoms } = await query.graph({
|
||||
entryPoint: "my_custom",
|
||||
fields: ["id", "name"],
|
||||
variables: {
|
||||
order: {
|
||||
name: "DESC",
|
||||
},
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
<Note>
|
||||
|
||||
Sorting doesn't work on fields of linked data models from other modules.
|
||||
|
||||
</Note>
|
||||
|
||||
To sort returned records, pass an `order` property to `variables`.
|
||||
|
||||
The `order` property is an object whose keys are property names, and values are either:
|
||||
|
||||
- `ASC` to sort records by that property in ascending order.
|
||||
- `DESC` to sort records by that property in descending order.
|
||||
|
||||
---
|
||||
|
||||
## Apply Pagination
|
||||
|
||||
```ts highlights={[["8", "skip", "The number of records to skip before fetching the results."], ["9", "take", "The number of records to fetch."]]}
|
||||
const {
|
||||
data: myCustoms,
|
||||
metadata: { count, take, skip },
|
||||
} = await query.graph({
|
||||
entryPoint: "my_custom",
|
||||
fields: ["id", "name"],
|
||||
variables: {
|
||||
skip: 0,
|
||||
take: 10,
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
To paginate the returned records, pass the following properties to `variables`:
|
||||
|
||||
- `skip`: (required to apply pagination) The number of records to skip before fetching the results.
|
||||
- `take`: The number of records to fetch.
|
||||
|
||||
When you provide the pagination fields, the `query.graph` method's returned object has a `metadata` property. Its value is an object having the following properties:
|
||||
|
||||
<TypeList types={[
|
||||
{
|
||||
name: "skip",
|
||||
type: "`number`",
|
||||
description: "The number of records skipped."
|
||||
},
|
||||
{
|
||||
name: "take",
|
||||
type: "`number`",
|
||||
description: "The number of records requested to fetch."
|
||||
},
|
||||
{
|
||||
name: "count",
|
||||
type: "`number`",
|
||||
description: "The total number of records."
|
||||
}
|
||||
]} sectionTitle="Apply Pagination" />
|
||||
@@ -1,410 +0,0 @@
|
||||
import { TypeList, Tabs, TabsList, TabsTriggerVertical, TabsContent, TabsContentWrapper } from "docs-ui"
|
||||
|
||||
export const metadata = {
|
||||
title: `${pageNumber} Remote Query`,
|
||||
}
|
||||
|
||||
# {metadata.title}
|
||||
|
||||
In this chapter, you’ll learn about the remote query and how to use it to fetch data from modules.
|
||||
|
||||
## What is the Remote Query?
|
||||
|
||||
The remote query fetches data across modules. It’s a function registered in the Medusa container under the `remoteQuery` key.
|
||||
|
||||
In your resources, such as API routes or workflows, you can resolve the remote query to fetch data across custom modules and Medusa’s commerce modules.
|
||||
|
||||
---
|
||||
|
||||
## Remote Query Example
|
||||
|
||||
For example, create the route `src/api/store/query/route.ts` with the following content:
|
||||
|
||||
export const exampleHighlights = [
|
||||
["18", "", "Resolve the remote query from the Medusa container."],
|
||||
["21", "remoteQueryObjectFromString", "Utility function to build the query."],
|
||||
["22", "entryPoint", "The name of the data model you're querying."],
|
||||
["23", "fields", "An array of the data model’s properties to retrieve in the result."],
|
||||
["27", "remoteQuery", "Run the query using the remote query."]
|
||||
]
|
||||
|
||||
```ts title="src/api/store/query/route.ts" highlights={exampleHighlights} apiTesting testApiMethod="GET" testApiUrl="http://localhost:9000/store/query" collapsibleLines="1-12" expandButtonLabel="Show Imports"
|
||||
import {
|
||||
MedusaRequest,
|
||||
MedusaResponse,
|
||||
} from "@medusajs/medusa"
|
||||
import {
|
||||
remoteQueryObjectFromString,
|
||||
ContainerRegistrationKeys,
|
||||
} from "@medusajs/utils"
|
||||
import type {
|
||||
RemoteQueryFunction,
|
||||
} from "@medusajs/modules-sdk"
|
||||
|
||||
export async function GET(
|
||||
req: MedusaRequest,
|
||||
res: MedusaResponse
|
||||
): Promise<void> {
|
||||
const remoteQuery: RemoteQueryFunction = req.scope.resolve(
|
||||
ContainerRegistrationKeys.REMOTE_QUERY
|
||||
)
|
||||
|
||||
const query = remoteQueryObjectFromString({
|
||||
entryPoint: "my_custom",
|
||||
fields: ["id", "name"],
|
||||
})
|
||||
|
||||
res.json({
|
||||
my_customs: await remoteQuery(query),
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
In the above example, you resolve `remoteQuery` from the Medusa container.
|
||||
|
||||
Then, you create a query using the `remoteQueryObjectFromString` utility function imported from `@medusajs/utils`. This function accepts as a parameter an object with the following required properties:
|
||||
|
||||
- `entryPoint`: The data model's name, as specified in the first parameter of the `model.define` method used for the data model's definition.
|
||||
- `fields`: An array of the data model’s properties to retrieve in the result.
|
||||
|
||||
You then pass the query to the `remoteQuery` function to retrieve the results.
|
||||
|
||||
---
|
||||
|
||||
## Retrieve Linked Records
|
||||
|
||||
Retrieve the records of a linked data model by passing in `fields` the data model's name suffixed with `.*`.
|
||||
|
||||
For example:
|
||||
|
||||
```ts highlights={[["6"]]}
|
||||
const query = remoteQueryObjectFromString({
|
||||
entryPoint: "my_custom",
|
||||
fields: [
|
||||
"id",
|
||||
"name",
|
||||
"product.*",
|
||||
],
|
||||
})
|
||||
```
|
||||
|
||||
<Note title="Tip">
|
||||
|
||||
`.*` means that all of data model's properties should be retrieved. To retrieve a specific property, replace the `*` with the property's name. For example, `product.title`.
|
||||
|
||||
</Note>
|
||||
|
||||
### Retrieve List Link Records
|
||||
|
||||
If the linked data model has `isList` enabled in the link definition, pass in `fields` the data model's plural name suffixed with `.*`.
|
||||
|
||||
For example:
|
||||
|
||||
```ts highlights={[["6"]]}
|
||||
const query = remoteQueryObjectFromString({
|
||||
entryPoint: "my_custom",
|
||||
fields: [
|
||||
"id",
|
||||
"name",
|
||||
"products.*",
|
||||
],
|
||||
})
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Apply Filters
|
||||
|
||||
```ts highlights={[["6"], ["7"], ["8"], ["9"]]}
|
||||
const query = remoteQueryObjectFromString({
|
||||
entryPoint: "my_custom",
|
||||
fields: ["id", "name"],
|
||||
variables: {
|
||||
filters: {
|
||||
id: [
|
||||
"mc_01HWSVWR4D2XVPQ06DQ8X9K7AX",
|
||||
"mc_01HWSVWK3KYHKQEE6QGS2JC3FX",
|
||||
],
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
const result = await remoteQuery(query)
|
||||
```
|
||||
|
||||
The `remoteQueryObjectFromString` function accepts a `variables` property. You can use this property to filter retrieved records.
|
||||
|
||||
<TypeList
|
||||
types={[
|
||||
{
|
||||
name: "variables",
|
||||
type: "`object`",
|
||||
description: "Variables to pass to the query.",
|
||||
children: [
|
||||
{
|
||||
name: "filters",
|
||||
type: "`object`",
|
||||
description: "The filters to apply on any of the data model's properties."
|
||||
}
|
||||
]
|
||||
},
|
||||
]}
|
||||
sectionTitle="Apply Filters"
|
||||
/>
|
||||
|
||||
---
|
||||
|
||||
## Sort Records
|
||||
|
||||
```ts highlights={[["5"], ["6"], ["7"]]}
|
||||
const query = remoteQueryObjectFromString({
|
||||
entryPoint: "my_custom",
|
||||
fields: ["id", "name"],
|
||||
variables: {
|
||||
order: {
|
||||
name: "DESC",
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
const result = await remoteQuery(query)
|
||||
```
|
||||
|
||||
To sort returned records, pass an `order` property to `variables`.
|
||||
|
||||
The `order` property is an object whose keys are property names, and values are either:
|
||||
|
||||
- `ASC` to sort records by that property in ascending order.
|
||||
- `DESC` to sort records by that property in descending order.
|
||||
|
||||
---
|
||||
|
||||
## Apply Pagination
|
||||
|
||||
```ts highlights={[["5", "skip", "The number of records to skip before fetching the results."], ["6", "take", "The number of records to fetch."]]}
|
||||
const query = remoteQueryObjectFromString({
|
||||
entryPoint: "my_custom",
|
||||
fields: ["id", "name"],
|
||||
variables: {
|
||||
skip: 0,
|
||||
take: 10,
|
||||
},
|
||||
})
|
||||
|
||||
const {
|
||||
rows,
|
||||
metadata: { count, take, skip },
|
||||
} = await remoteQuery(query)
|
||||
```
|
||||
|
||||
To paginate the returned records, pass the following properties to `variables`:
|
||||
|
||||
- `skip`: (required to apply pagination) The number of records to skip before fetching the results.
|
||||
- `take`: The number of records to fetch.
|
||||
|
||||
When the pagination fields are provided, the `remoteQuery` returns an object having two properties:
|
||||
|
||||
<TypeList types={[
|
||||
{
|
||||
name: "rows",
|
||||
type: "`array`",
|
||||
description: "The returned records."
|
||||
},
|
||||
{
|
||||
name: "metadata",
|
||||
type: "`object`",
|
||||
description: "The pagination details",
|
||||
children: [
|
||||
{
|
||||
name: "skip",
|
||||
type: "`number`",
|
||||
description: "The number of records skipped."
|
||||
},
|
||||
{
|
||||
name: "take",
|
||||
type: "`number`",
|
||||
description: "The number of records requested to fetch."
|
||||
},
|
||||
{
|
||||
name: "count",
|
||||
type: "`number`",
|
||||
description: "The total number of records."
|
||||
}
|
||||
]
|
||||
}
|
||||
]} sectionTitle="Apply Pagination" />
|
||||
|
||||
---
|
||||
|
||||
## Using GraphQL
|
||||
|
||||
The remote query function alternatively accepts a string with GraphQL syntax as the query.
|
||||
|
||||
<Tabs defaultValue="basic" layoutType="vertical" className="mt-2">
|
||||
<TabsList>
|
||||
<TabsTriggerVertical value="basic">Basic Usage</TabsTriggerVertical>
|
||||
<TabsTriggerVertical value="filters">Apply Filters</TabsTriggerVertical>
|
||||
<TabsTriggerVertical value="sort">Sort Records</TabsTriggerVertical>
|
||||
<TabsTriggerVertical value="pagination">Apply Pagination</TabsTriggerVertical>
|
||||
</TabsList>
|
||||
<TabsContentWrapper>
|
||||
<TabsContent value="basic" className="[&_h3]:!mt-0">
|
||||
|
||||
### Basic GraphQL usage
|
||||
|
||||
```ts title="src/api/store/query/route.ts" apiTesting testApiMethod="GET" testApiUrl="http://localhost:9000/store/query" collapsibleLines="1-10" expandButtonLabel="Show Imports"
|
||||
import {
|
||||
MedusaRequest,
|
||||
MedusaResponse,
|
||||
} from "@medusajs/medusa"
|
||||
import { ContainerRegistrationKeys } from "@medusajs/utils"
|
||||
import type {
|
||||
RemoteQueryFunction,
|
||||
} from "@medusajs/modules-sdk"
|
||||
|
||||
export async function GET(
|
||||
req: MedusaRequest,
|
||||
res: MedusaResponse
|
||||
): Promise<void> {
|
||||
const remoteQuery: RemoteQueryFunction = req.scope.resolve(
|
||||
ContainerRegistrationKeys.REMOTE_QUERY
|
||||
)
|
||||
|
||||
const query = `
|
||||
query {
|
||||
my_custom {
|
||||
id
|
||||
name
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
const result = await remoteQuery(query)
|
||||
|
||||
res.json({
|
||||
my_customs: result,
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
</TabsContent>
|
||||
<TabsContent value="filters" className="[&_h3]:!mt-0">
|
||||
|
||||
### Apply Filters with GraphQL
|
||||
|
||||
The `remoteQuery` function accepts as a second parameter an object of variables to reference in the GraphQL query.
|
||||
|
||||
```ts highlights={[["2"], ["3"], ["13"], ["14"], ["15"], ["16"]]}
|
||||
const query = `
|
||||
query($id: ID) {
|
||||
my_custom(id: $id) {
|
||||
id
|
||||
name
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
const result = await remoteQuery(
|
||||
query,
|
||||
{
|
||||
id: [
|
||||
"mc_01HWSVWR4D2XVPQ06DQ8X9K7AX",
|
||||
"mc_01HWSVWK3KYHKQEE6QGS2JC3FX",
|
||||
]
|
||||
}
|
||||
)
|
||||
```
|
||||
|
||||
</TabsContent>
|
||||
<TabsContent value="sort" className="[&_h3]:!mt-0">
|
||||
|
||||
### Sort Records with GraphQL
|
||||
|
||||
To sort the records by a property, pass in the query an `order` argument whose value is an object. The object’s key is the property’s name, and the value is either:
|
||||
|
||||
- `ASC` to sort items by that property in ascending order.
|
||||
- `DESC` to sort items by that property in descending order.
|
||||
|
||||
For example:
|
||||
|
||||
```ts highlights={[["3"]]}
|
||||
const query = `
|
||||
query {
|
||||
my_custom(order: {name: DESC}) {
|
||||
id
|
||||
name
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
const result = await remoteQuery(query)
|
||||
```
|
||||
|
||||
</TabsContent>
|
||||
<TabsContent value="pagination" className="[&_h3]:!mt-0">
|
||||
|
||||
### Pagination with GraphQL
|
||||
|
||||
To paginate the records retrieved, pass a `skip` and `take` records in your query, and pass their values in the second parameter of the `remoteQuery` function.
|
||||
|
||||
For example:
|
||||
|
||||
```ts highlights={[["2"], ["3"]]}
|
||||
const query = `
|
||||
query($skip: Int, $take: Int) {
|
||||
my_custom(skip: $skip, take: $take) {
|
||||
id
|
||||
name
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
const {
|
||||
rows,
|
||||
metadata: { count, take, skip }
|
||||
} = await remoteQuery(
|
||||
query,
|
||||
{
|
||||
skip: 0,
|
||||
take: 10
|
||||
}
|
||||
)
|
||||
```
|
||||
|
||||
This skips no records and returns the first `10` records.
|
||||
|
||||
When the pagination fields are provided, the `remoteQuery` returns an object having two properties:
|
||||
|
||||
<TypeList types={[
|
||||
{
|
||||
name: "rows",
|
||||
type: "`array`",
|
||||
description: "The returned records."
|
||||
},
|
||||
{
|
||||
name: "metadata",
|
||||
type: "`object`",
|
||||
description: "The pagination details",
|
||||
children: [
|
||||
{
|
||||
name: "skip",
|
||||
type: "`number`",
|
||||
description: "The number of records skipped."
|
||||
},
|
||||
{
|
||||
name: "take",
|
||||
type: "`number`",
|
||||
description: "The number of records requested to fetch."
|
||||
},
|
||||
{
|
||||
name: "count",
|
||||
type: "`number`",
|
||||
description: "The total number of records."
|
||||
}
|
||||
]
|
||||
}
|
||||
]} sectionTitle="Pagination with GraphQL" />
|
||||
|
||||
</TabsContent>
|
||||
</TabsContentWrapper>
|
||||
</Tabs>
|
||||
@@ -94,7 +94,7 @@ This property is an object that holds additional data passed to the workflow.
|
||||
To pass that additional data when executing the workflow, pass it as a parameter to the `.run` method of the workflow:
|
||||
|
||||
```ts highlights={[["10", "additional_data"]]}
|
||||
import type { MedusaRequest, MedusaResponse } from "@medusajs/medusa";
|
||||
import type { MedusaRequest, MedusaResponse } from "@medusajs/medusa"
|
||||
import { createProductsWorkflow } from "@medusajs/core-flows"
|
||||
|
||||
export async function POST(req: MedusaRequest, res: MedusaResponse) {
|
||||
@@ -104,8 +104,8 @@ export async function POST(req: MedusaRequest, res: MedusaResponse) {
|
||||
// ...
|
||||
],
|
||||
additional_data: {
|
||||
custom_field: "test"
|
||||
}
|
||||
custom_field: "test",
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ export async function GET(
|
||||
res: MedusaResponse
|
||||
){
|
||||
res.json({
|
||||
message: "Hello, World!"
|
||||
message: "Hello, World!",
|
||||
})
|
||||
}
|
||||
```
|
||||
@@ -58,7 +58,7 @@ medusaIntegrationTestRunner({
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
@@ -107,7 +107,7 @@ And consider that the file `src/api/store/custom/route.ts` defines another route
|
||||
|
||||
```ts title="src/api/store/custom/route.ts"
|
||||
// other imports...
|
||||
import HelloModuleService from "../../../modules/hello/service";
|
||||
import HelloModuleService from "../../../modules/hello/service"
|
||||
|
||||
// ...
|
||||
|
||||
@@ -124,7 +124,7 @@ export async function POST(
|
||||
)
|
||||
|
||||
res.json({
|
||||
my_custom: myCustom
|
||||
my_custom: myCustom,
|
||||
})
|
||||
}
|
||||
```
|
||||
@@ -155,7 +155,7 @@ medusaIntegrationTestRunner({
|
||||
`/store/custom`,
|
||||
{
|
||||
id,
|
||||
name: "Test"
|
||||
name: "Test",
|
||||
}
|
||||
)
|
||||
|
||||
@@ -170,7 +170,7 @@ medusaIntegrationTestRunner({
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
@@ -210,7 +210,7 @@ medusaIntegrationTestRunner({
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
@@ -223,8 +223,8 @@ The `afterAll` hook resolves the `HelloModuleService` and use its `deleteMyCusto
|
||||
Consider a `/store/custom/:id` API route created at `src/api/store/custom/[id]/route.ts`:
|
||||
|
||||
```ts title="src/api/store/custom/[id]/route.ts"
|
||||
import { MedusaRequest, MedusaResponse } from "@medusajs/medusa";
|
||||
import HelloModuleService from "../../../modules/hello/service";
|
||||
import { MedusaRequest, MedusaResponse } from "@medusajs/medusa"
|
||||
import HelloModuleService from "../../../modules/hello/service"
|
||||
|
||||
export async function DELETE(
|
||||
req: MedusaRequest,
|
||||
@@ -237,7 +237,7 @@ export async function DELETE(
|
||||
await helloModuleService.deleteMyCustoms(req.params.id)
|
||||
|
||||
res.json({
|
||||
success: true
|
||||
success: true,
|
||||
})
|
||||
}
|
||||
```
|
||||
@@ -266,7 +266,7 @@ medusaIntegrationTestRunner({
|
||||
|
||||
await helloModuleService.createMyCustoms({
|
||||
id,
|
||||
name: "Test"
|
||||
name: "Test",
|
||||
})
|
||||
})
|
||||
|
||||
@@ -281,7 +281,7 @@ medusaIntegrationTestRunner({
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@ import { medusaIntegrationTestRunner } from "medusa-test-utils"
|
||||
medusaIntegrationTestRunner({
|
||||
testSuite: ({ api, getContainer }) => {
|
||||
// TODO write tests...
|
||||
}
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ import {
|
||||
createWorkflow,
|
||||
createStep,
|
||||
StepResponse,
|
||||
WorkflowResponse
|
||||
WorkflowResponse,
|
||||
} from "@medusajs/workflows-sdk"
|
||||
|
||||
const step1 = createStep("step-1", () => {
|
||||
@@ -59,7 +59,7 @@ medusaIntegrationTestRunner({
|
||||
expect(result).toEqual("Hello, World!")
|
||||
})
|
||||
})
|
||||
}
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
|
||||
@@ -56,7 +56,7 @@ moduleIntegrationTestRunner<HelloModuleService>({
|
||||
expect(message).toEqual("Hello, World!")
|
||||
})
|
||||
})
|
||||
}
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ moduleIntegrationTestRunner<HelloModuleService>({
|
||||
resolve: "./modules/hello",
|
||||
testSuite: ({ service }) => {
|
||||
// TODO write tests
|
||||
}
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
@@ -88,7 +88,7 @@ import HelloModuleService from "../service"
|
||||
|
||||
moduleIntegrationTestRunner<HelloModuleService>({
|
||||
moduleOptions: {
|
||||
apiKey: "123"
|
||||
apiKey: "123",
|
||||
},
|
||||
// ...
|
||||
})
|
||||
@@ -108,7 +108,7 @@ import HelloModuleService from "../service"
|
||||
import { model } from "@medusajs/utils"
|
||||
|
||||
const DummyModel = model.define("dummy_model", {
|
||||
id: model.id().primaryKey()
|
||||
id: model.id().primaryKey(),
|
||||
})
|
||||
|
||||
moduleIntegrationTestRunner<HelloModuleService>({
|
||||
|
||||
@@ -41,8 +41,8 @@ npm install --save-dev jest @types/jest @swc/jest
|
||||
Then, create the file `jest.config.js` with the following content:
|
||||
|
||||
```js title="jest.config.js"
|
||||
const { loadEnv } = require('@medusajs/utils')
|
||||
loadEnv('test', process.cwd())
|
||||
const { loadEnv } = require("@medusajs/utils")
|
||||
loadEnv("test", process.cwd())
|
||||
|
||||
module.exports = {
|
||||
transform: {
|
||||
|
||||
@@ -6,7 +6,7 @@ export const generatedEditDates = {
|
||||
"app/basics/modules-and-services/page.mdx": "2024-09-03T07:45:28.079Z",
|
||||
"app/basics/commerce-modules/page.mdx": "2024-09-03T07:48:48.148Z",
|
||||
"app/advanced-development/workflows/retry-failed-steps/page.mdx": "2024-07-31T17:01:33+03:00",
|
||||
"app/advanced-development/workflows/workflow-hooks/page.mdx": "2024-08-13T09:55:37+03:00",
|
||||
"app/advanced-development/workflows/workflow-hooks/page.mdx": "2024-09-10T11:39:51.168Z",
|
||||
"app/cheatsheet/page.mdx": "2024-07-11T13:53:40+03:00",
|
||||
"app/debugging-and-testing/logging/page.mdx": "2024-07-04T17:26:03+03:00",
|
||||
"app/more-resources/page.mdx": "2024-07-04T17:26:03+03:00",
|
||||
@@ -33,19 +33,19 @@ export const generatedEditDates = {
|
||||
"app/advanced-development/admin/widgets/page.mdx": "2024-08-06T09:44:22+02:00",
|
||||
"app/advanced-development/data-models/page.mdx": "2024-07-04T17:26:03+03:00",
|
||||
"app/advanced-development/modules/remote-link/page.mdx": "2024-07-24T09:16:01+02:00",
|
||||
"app/advanced-development/api-routes/protected-routes/page.mdx": "2024-09-04T10:11:25.860Z",
|
||||
"app/advanced-development/api-routes/protected-routes/page.mdx": "2024-09-10T11:39:51.166Z",
|
||||
"app/advanced-development/workflows/add-workflow-hook/page.mdx": "2024-08-13T09:55:37+03:00",
|
||||
"app/advanced-development/events-and-subscribers/data-payload/page.mdx": "2024-07-16T17:12:05+01:00",
|
||||
"app/advanced-development/data-models/default-properties/page.mdx": "2024-07-02T12:34:44+03:00",
|
||||
"app/advanced-development/workflows/advanced-example/page.mdx": "2024-07-31T17:01:33+03:00",
|
||||
"app/advanced-development/events-and-subscribers/emit-event/page.mdx": "2024-08-05T11:39:47+03:00",
|
||||
"app/advanced-development/events-and-subscribers/emit-event/page.mdx": "2024-09-10T11:39:51.168Z",
|
||||
"app/advanced-development/workflows/conditions/page.mdx": "2024-07-31T17:01:33+03:00",
|
||||
"app/advanced-development/modules/module-link-directions/page.mdx": "2024-07-24T09:16:01+02:00",
|
||||
"app/advanced-development/admin/page.mdx": "2024-05-29T13:50:19+03:00",
|
||||
"app/advanced-development/workflows/long-running-workflow/page.mdx": "2024-07-31T17:01:33+03:00",
|
||||
"app/advanced-development/workflows/constructor-constraints/page.mdx": "2024-07-17T13:19:51+01:00",
|
||||
"app/advanced-development/data-models/write-migration/page.mdx": "2024-07-15T17:46:10+02:00",
|
||||
"app/advanced-development/data-models/manage-relationships/page.mdx": "2024-08-15T16:30:00+03:00",
|
||||
"app/advanced-development/data-models/manage-relationships/page.mdx": "2024-09-10T11:39:51.167Z",
|
||||
"app/advanced-development/modules/remote-query/page.mdx": "2024-07-21T21:20:24+02:00",
|
||||
"app/advanced-development/modules/options/page.mdx": "2024-08-05T07:23:49+00:00",
|
||||
"app/advanced-development/data-models/relationships/page.mdx": "2024-08-15T16:30:00+03:00",
|
||||
@@ -57,7 +57,7 @@ export const generatedEditDates = {
|
||||
"app/advanced-development/scheduled-jobs/execution-number/page.mdx": "2024-07-02T09:41:15+00:00",
|
||||
"app/advanced-development/api-routes/parameters/page.mdx": "2024-09-04T08:17:50.071Z",
|
||||
"app/advanced-development/api-routes/http-methods/page.mdx": "2024-09-04T08:15:11.609Z",
|
||||
"app/advanced-development/admin/tips/page.mdx": "2024-08-05T13:20:34+03:00",
|
||||
"app/advanced-development/admin/tips/page.mdx": "2024-09-10T11:39:51.165Z",
|
||||
"app/advanced-development/api-routes/cors/page.mdx": "2024-09-04T08:24:47.068Z",
|
||||
"app/advanced-development/admin/ui-routes/page.mdx": "2024-08-06T09:44:22+02:00",
|
||||
"app/advanced-development/api-routes/middlewares/page.mdx": "2024-09-04T09:45:12.441Z",
|
||||
@@ -66,14 +66,18 @@ export const generatedEditDates = {
|
||||
"app/advanced-development/data-models/index/page.mdx": "2024-07-04T17:26:03+03:00",
|
||||
"app/advanced-development/custom-cli-scripts/page.mdx": "2024-07-04T17:26:03+03:00",
|
||||
"app/advanced-development/data-models/property-types/page.mdx": "2024-07-04T17:26:03+03:00",
|
||||
"app/debugging-and-testing/testing-tools/integration-tests/api-routes/page.mdx": "2024-09-02T10:57:08.040Z",
|
||||
"app/debugging-and-testing/testing-tools/integration-tests/page.mdx": "2024-09-02T10:56:09.872Z",
|
||||
"app/debugging-and-testing/testing-tools/integration-tests/workflows/page.mdx": "2024-09-02T10:57:04.202Z",
|
||||
"app/debugging-and-testing/testing-tools/page.mdx": "2024-09-02T10:08:29.388Z",
|
||||
"app/debugging-and-testing/testing-tools/integration-tests/api-routes/page.mdx": "2024-09-10T11:39:51.170Z",
|
||||
"app/debugging-and-testing/testing-tools/integration-tests/page.mdx": "2024-09-10T11:39:51.170Z",
|
||||
"app/debugging-and-testing/testing-tools/integration-tests/workflows/page.mdx": "2024-09-10T11:39:51.171Z",
|
||||
"app/debugging-and-testing/testing-tools/page.mdx": "2024-09-10T11:39:51.172Z",
|
||||
"app/debugging-and-testing/testing-tools/unit-tests/module-example/page.mdx": "2024-09-02T11:04:27.232Z",
|
||||
"app/debugging-and-testing/testing-tools/unit-tests/page.mdx": "2024-09-02T11:03:26.997Z",
|
||||
"app/advanced-development/api-routes/page.mdx": "2024-09-04T09:36:33.961Z",
|
||||
"app/advanced-development/api-routes/responses/page.mdx": "2024-09-04T09:40:38.986Z",
|
||||
"app/advanced-development/api-routes/validation/page.mdx": "2024-09-04T09:50:52.129Z",
|
||||
"app/advanced-development/api-routes/errors/page.mdx": "2024-09-04T11:03:55.017Z"
|
||||
"app/advanced-development/api-routes/responses/page.mdx": "2024-09-10T11:39:51.167Z",
|
||||
"app/advanced-development/api-routes/validation/page.mdx": "2024-09-10T11:39:51.167Z",
|
||||
"app/advanced-development/api-routes/errors/page.mdx": "2024-09-10T11:39:51.166Z",
|
||||
"app/advanced-development/admin/constraints/page.mdx": "2024-09-10T11:39:51.165Z",
|
||||
"app/advanced-development/modules/query/page.mdx": "2024-09-10T11:39:51.168Z",
|
||||
"app/debugging-and-testing/testing-tools/modules-tests/module-example/page.mdx": "2024-09-10T11:39:51.171Z",
|
||||
"app/debugging-and-testing/testing-tools/modules-tests/page.mdx": "2024-09-10T11:39:51.171Z"
|
||||
}
|
||||
@@ -127,6 +127,15 @@ const nextConfig = {
|
||||
],
|
||||
}
|
||||
},
|
||||
async redirects() {
|
||||
return [
|
||||
{
|
||||
source: "/advanced-development/modules/remote-query",
|
||||
destination: "/advanced-development/modules/query",
|
||||
permanent: true,
|
||||
},
|
||||
]
|
||||
},
|
||||
}
|
||||
|
||||
export default withMDX(nextConfig)
|
||||
|
||||
@@ -172,8 +172,8 @@ export const sidebar = numberSidebarItems(
|
||||
},
|
||||
{
|
||||
type: "link",
|
||||
path: "/advanced-development/modules/remote-query",
|
||||
title: "Remote Query",
|
||||
path: "/advanced-development/modules/query",
|
||||
title: "Query",
|
||||
},
|
||||
{
|
||||
type: "link",
|
||||
|
||||
@@ -14,17 +14,18 @@ In this document, you'll learn how to calculate a product variant's price with t
|
||||
|
||||
You'll need the following resources for the taxes calculation:
|
||||
|
||||
1. Remote query to retrieve the product's variants' prices for a context. Learn more about that in [this guide](../price/page.mdx).
|
||||
1. Query to retrieve the product's variants' prices for a context. Learn more about that in [this guide](../price/page.mdx).
|
||||
2. The Tax Module's main service to get the tax lines for each product.
|
||||
|
||||
```ts
|
||||
// other imports...
|
||||
import {
|
||||
ModuleRegistrationName,
|
||||
ContainerRegistrationKeys,
|
||||
} from "@medusajs/utils"
|
||||
|
||||
// In an API route, workflow step, etc...
|
||||
const remoteQuery = container.resolve("remoteQuery")
|
||||
const query = container.resolve(ContainerRegistrationKeys.QUERY)
|
||||
const taxModuleService = container.resolve(
|
||||
ModuleRegistrationName.TAX
|
||||
)
|
||||
@@ -34,16 +35,10 @@ const taxModuleService = container.resolve(
|
||||
|
||||
## Step 1: Retrieve Prices for a Context
|
||||
|
||||
After resolving the resources, use the remote query to retrieve the products with the variants' prices for a context:
|
||||
After resolving the resources, use Query to retrieve the products with the variants' prices for a context:
|
||||
|
||||
```ts
|
||||
// other imports...
|
||||
import {
|
||||
remoteQueryObjectFromString,
|
||||
} from "@medusajs/utils"
|
||||
|
||||
// ...
|
||||
const query = remoteQueryObjectFromString({
|
||||
const { data: products } = await query.graph({
|
||||
entryPoint: "product",
|
||||
fields: [
|
||||
"*",
|
||||
@@ -52,18 +47,16 @@ const query = remoteQueryObjectFromString({
|
||||
],
|
||||
variables: {
|
||||
filters: {
|
||||
id,
|
||||
id: "prod_123",
|
||||
},
|
||||
"variants.calculated_price": {
|
||||
context: {
|
||||
region_id,
|
||||
currency_code,
|
||||
region_id: "region_123",
|
||||
currency_code: "usd",
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
const products = await remoteQuery(query)
|
||||
```
|
||||
|
||||
<Note>
|
||||
|
||||
@@ -3,29 +3,29 @@ sidebar_label: "Get Product Variant Prices"
|
||||
---
|
||||
|
||||
export const metadata = {
|
||||
title: `Get Product Variant Prices using Remote Query`,
|
||||
title: `Get Product Variant Prices using Query`,
|
||||
}
|
||||
|
||||
# {metadata.title}
|
||||
|
||||
In this document, you'll learn how to retrieve product variant prices in the Medusa application using the [remote query](!docs!/advanced-development/modules/remote-query).
|
||||
In this document, you'll learn how to retrieve product variant prices in the Medusa application using the [Query](!docs!/advanced-development/modules/query).
|
||||
|
||||
<Note title="Why use the Remote Query?">
|
||||
<Note title="Why use Query?">
|
||||
|
||||
The Product Module doesn't provide pricing functionalities. The Medusa application links the Product Module's `ProductVariant` data model to the Pricing Module's `PriceSet` data model.
|
||||
|
||||
So, to retrieve data across the linked records of the two modules, you use the remote query.
|
||||
So, to retrieve data across the linked records of the two modules, you use Query.
|
||||
|
||||
</Note>
|
||||
|
||||
## Retrieve All Product Variant Prices
|
||||
|
||||
To retrieve all product variant prices, retrieve the product using the remote query and include among its fields `variants.prices.*`.
|
||||
To retrieve all product variant prices, retrieve the product using Query and include among its fields `variants.prices.*`.
|
||||
|
||||
For example:
|
||||
|
||||
```ts highlights={[["6"]]}
|
||||
const query = remoteQueryObjectFromString({
|
||||
const { data: products } = await query.graph({
|
||||
entryPoint: "product",
|
||||
fields: [
|
||||
"*",
|
||||
@@ -40,9 +40,6 @@ const query = remoteQueryObjectFromString({
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
// `result` is array of products
|
||||
const result = await remoteQuery(query)
|
||||
```
|
||||
|
||||
Each variant in the retrieved products has a `prices` array property with all the product variant prices. Each price object has the properties of the [Pricing Module's Price data model](/references/pricing/models/Price).
|
||||
@@ -59,7 +56,7 @@ Learn more about prices calculation in [this Pricing Module documentation](../..
|
||||
|
||||
</Note>
|
||||
|
||||
To retrieve calculated prices of variants based on a context, retrieve the products using remote query and:
|
||||
To retrieve calculated prices of variants based on a context, retrieve the products using Query and:
|
||||
|
||||
- Pass `variants.calculated_price.*` in the `fields` property.
|
||||
- Pass in the `variables` property a `variants.calculated_price` property whose value is the [calculation context object](../../../pricing/price-calculation/page.mdx#calculation-context).
|
||||
@@ -67,7 +64,7 @@ To retrieve calculated prices of variants based on a context, retrieve the produ
|
||||
For example:
|
||||
|
||||
```ts highlights={[["6"], ["12"], ["13"], ["14"], ["15"], ["16"], ["17"]]}
|
||||
const query = remoteQueryObjectFromString({
|
||||
const { data: products } = await query.graph({
|
||||
entryPoint: "product",
|
||||
fields: [
|
||||
"*",
|
||||
@@ -76,7 +73,7 @@ const query = remoteQueryObjectFromString({
|
||||
],
|
||||
variables: {
|
||||
filters: {
|
||||
id,
|
||||
id: "prod_123",
|
||||
},
|
||||
"variants.calculated_price": {
|
||||
context: {
|
||||
@@ -86,9 +83,6 @@ const query = remoteQueryObjectFromString({
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
// `result` is array of products
|
||||
const result = await remoteQuery(query)
|
||||
```
|
||||
|
||||
The `variants.calculated_price` property of `variables` is an object that has a `context` property. `context`'s value is an object whose keys are price rules, such as `region_id`, and value is the rule's value in this context, such as the customer's region's ID.
|
||||
|
||||
@@ -8,6 +8,12 @@ export const metadata = {
|
||||
|
||||
This documentation page includes the list of resources registered in the Medusa container of your Medusa application.
|
||||
|
||||
<Note>
|
||||
|
||||
Use the `ContainerRegistrationKeys` enum imported from `@medusajs/utils` to resolve these resources' names.
|
||||
|
||||
</Note>
|
||||
|
||||
<Table>
|
||||
<Table.Header>
|
||||
<Table.Row>
|
||||
@@ -43,7 +49,7 @@ This documentation page includes the list of resources registered in the Medusa
|
||||
</Table.Cell>
|
||||
<Table.Cell>
|
||||
|
||||
`configModule`
|
||||
`configModule` or `ContainerRegistrationKeys.CONFIG_MODULE`
|
||||
|
||||
</Table.Cell>
|
||||
</Table.Row>
|
||||
@@ -61,7 +67,7 @@ This documentation page includes the list of resources registered in the Medusa
|
||||
</Table.Cell>
|
||||
<Table.Cell>
|
||||
|
||||
`logger`
|
||||
`logger` or `ContainerRegistrationKeys.LOGGER`
|
||||
|
||||
</Table.Cell>
|
||||
</Table.Row>
|
||||
@@ -69,17 +75,17 @@ This documentation page includes the list of resources registered in the Medusa
|
||||
<Table.Row>
|
||||
<Table.Cell>
|
||||
|
||||
Remote Query
|
||||
Query
|
||||
|
||||
</Table.Cell>
|
||||
<Table.Cell>
|
||||
|
||||
The remote query function.
|
||||
The Query utility methods.
|
||||
|
||||
</Table.Cell>
|
||||
<Table.Cell>
|
||||
|
||||
`remoteQuery`
|
||||
`query` or `ContainerRegistrationKeys.QUERY`
|
||||
|
||||
</Table.Cell>
|
||||
</Table.Row>
|
||||
@@ -97,7 +103,7 @@ This documentation page includes the list of resources registered in the Medusa
|
||||
</Table.Cell>
|
||||
<Table.Cell>
|
||||
|
||||
`remoteLink`
|
||||
`remoteLink` or `ContainerRegistrationKeys.REMOTE_LINK`
|
||||
|
||||
</Table.Cell>
|
||||
</Table.Row>
|
||||
|
||||
@@ -288,7 +288,7 @@ import {
|
||||
AuthenticatedMedusaRequest,
|
||||
MedusaResponse,
|
||||
} from "@medusajs/medusa"
|
||||
import { remoteQueryObjectFromString } from "@medusajs/utils"
|
||||
import { ContainerRegistrationKeys } from "@medusajs/utils"
|
||||
|
||||
export const GET = async (
|
||||
req: AuthenticatedMedusaRequest,
|
||||
@@ -299,9 +299,12 @@ export const GET = async (
|
||||
limit = 20,
|
||||
offset = 0,
|
||||
} = req.validatedQuery || {}
|
||||
const remoteQuery = req.scope.resolve("remoteQuery")
|
||||
const query = req.scope.resolve(ContainerRegistrationKeys.QUERY)
|
||||
|
||||
const query = remoteQueryObjectFromString({
|
||||
const {
|
||||
data: digitalProducts,
|
||||
metadata: { count, take, skip },
|
||||
} = await query.graph({
|
||||
entryPoint: "digital_product",
|
||||
fields: [
|
||||
"*",
|
||||
@@ -315,13 +318,8 @@ export const GET = async (
|
||||
},
|
||||
})
|
||||
|
||||
const {
|
||||
rows,
|
||||
metadata: { count, take, skip },
|
||||
} = await remoteQuery(query)
|
||||
|
||||
res.json({
|
||||
digital_products: rows,
|
||||
digital_products: digitalProducts,
|
||||
count,
|
||||
limit: take,
|
||||
offset: skip,
|
||||
@@ -331,7 +329,7 @@ export const GET = async (
|
||||
|
||||
This adds a `GET` API route at `/admin/digital-products`.
|
||||
|
||||
In the route handler, you use the remote query to retrieve the list of digital products and their relations. The route handler also supports pagination.
|
||||
In the route handler, you use Query to retrieve the list of digital products and their relations. The route handler also supports pagination.
|
||||
|
||||
### Test API Route
|
||||
|
||||
@@ -364,7 +362,7 @@ Make sure to replace `{token}` with the JWT token you retrieved.
|
||||
### Further Reads
|
||||
|
||||
- [How to Create an API Route](!docs!/basics/api-routes)
|
||||
- [Learn more about the remote query](!docs!/advanced-development/modules/remote-query)
|
||||
- [Learn more about Query](!docs!/advanced-development/modules/query)
|
||||
|
||||
---
|
||||
|
||||
@@ -1830,7 +1828,7 @@ Create the file `digital-product/src/subscribers/handle-digital-order.ts` with t
|
||||
export const subscriberHighlight = [
|
||||
["20", "notificationModuleService", "Resolve the Notification Module's service to use it later to send a notification."],
|
||||
["22", "fileModuleService", "Resolve the File Module's service to use it later to retrieve a media's URL."],
|
||||
["26", "query", "Assemble the query to retrieve the digital product order."]
|
||||
["26", "query", "Run the query to retrieve the digital product order."]
|
||||
]
|
||||
|
||||
```ts title="digital-product/src/subscribers/handle-digital-order.ts" highlights={subscriberHighlight} collapsibleLines="1-14" expandMoreLabel="Show Imports"
|
||||
@@ -1844,7 +1842,7 @@ import {
|
||||
} from "@medusajs/types"
|
||||
import {
|
||||
ModuleRegistrationName,
|
||||
remoteQueryObjectFromString,
|
||||
ContainerRegistrationKeys,
|
||||
} from "@medusajs/utils"
|
||||
import { MediaType } from "../modules/digital-product/types"
|
||||
|
||||
@@ -1852,14 +1850,14 @@ async function digitalProductOrderCreatedHandler({
|
||||
event: { data },
|
||||
container,
|
||||
}: SubscriberArgs<{ id: string }>) {
|
||||
const remoteQuery = container.resolve("remoteQuery")
|
||||
const query = container.resolve(ContainerRegistrationKeys.QUERY)
|
||||
const notificationModuleService: INotificationModuleService = container
|
||||
.resolve(ModuleRegistrationName.NOTIFICATION)
|
||||
const fileModuleService: IFileModuleService = container.resolve(
|
||||
ModuleRegistrationName.FILE
|
||||
)
|
||||
|
||||
const query = remoteQueryObjectFromString({
|
||||
const { data: [digitalProductOrder] } = await query.graph({
|
||||
entryPoint: "digital_product_order",
|
||||
fields: [
|
||||
"*",
|
||||
@@ -1874,8 +1872,6 @@ async function digitalProductOrderCreatedHandler({
|
||||
},
|
||||
})
|
||||
|
||||
const digitalProductOrder = (await remoteQuery(query))[0]
|
||||
|
||||
// TODO format and send notification
|
||||
}
|
||||
|
||||
@@ -2031,7 +2027,7 @@ You return in the response the preview files.
|
||||
Create the file `src/api/store/customers/me/digital-products/route.ts` with the following content:
|
||||
|
||||
export const purchasedDpHighlights = [
|
||||
["15", "remoteQueryObjectFromString", "Retrieve the customer's purchased digital products."]
|
||||
["15", "graph", "Retrieve the customer's purchased digital products."]
|
||||
]
|
||||
|
||||
```ts title="src/api/store/customers/me/digital-products/route.ts" highlights={purchasedDpHighlights} collapsibleLines="1-8" expandMoreLabel="Show Imports"
|
||||
@@ -2040,16 +2036,16 @@ import {
|
||||
MedusaResponse,
|
||||
} from "@medusajs/medusa"
|
||||
import {
|
||||
remoteQueryObjectFromString,
|
||||
ContainerRegistrationKeys,
|
||||
} from "@medusajs/utils"
|
||||
|
||||
export const GET = async (
|
||||
req: AuthenticatedMedusaRequest,
|
||||
res: MedusaResponse
|
||||
) => {
|
||||
const remoteQuery = req.scope.resolve("remoteQuery")
|
||||
const query = req.scope.resolve(ContainerRegistrationKeys.QUERY)
|
||||
|
||||
const query = remoteQueryObjectFromString({
|
||||
const { data: [customer] } = await query.graph({
|
||||
entryPoint: "customer",
|
||||
fields: [
|
||||
"orders.digital_product_order.products.*",
|
||||
@@ -2062,11 +2058,9 @@ export const GET = async (
|
||||
},
|
||||
})
|
||||
|
||||
const result = await remoteQuery(query)
|
||||
|
||||
const digitalProducts = {}
|
||||
|
||||
result[0].orders.forEach((order) => {
|
||||
customer.orders.forEach((order) => {
|
||||
order.digital_product_order.products.forEach((product) => {
|
||||
digitalProducts[product.id] = product
|
||||
})
|
||||
@@ -2076,23 +2070,22 @@ export const GET = async (
|
||||
digital_products: Object.values(digitalProducts),
|
||||
})
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
This adds a `GET` API route at `/store/customers/me/digital-products`. All API routes under `/store/customers/me` require customer authentication.
|
||||
|
||||
In the route handler, you use remote query to retrieve the customer’s orders and linked digital product orders, and return the purchased digital products in the response.
|
||||
In the route handler, you use Query to retrieve the customer’s orders and linked digital product orders, and return the purchased digital products in the response.
|
||||
|
||||
### Get Digital Product Media Download URL API Route
|
||||
|
||||
Create the file `src/api/store/customers/me/digital-products/[mediaId]/download/route.ts` with the following content:
|
||||
|
||||
export const downloadUrlHighlights = [
|
||||
["20", "remoteQueryObjectFromString", "Get the customer's orders and linked digital orders."],
|
||||
["37", "remoteQueryObjectFromString", "Get the digital product orders of the customer and associated products and media."],
|
||||
["62", "foundMedia", "Set `foundMedia` if the media's ID is equal to the ID passed as a route parameter."],
|
||||
["68", "!foundMedia", "If `foundMedia` isn't set, throw an error."],
|
||||
["75", "retrieveFile", "Retrieve the details of the media's file."]
|
||||
["20", "query.graph", "Get the customer's orders and linked digital orders."],
|
||||
["36", "query.graph", "Get the digital product orders of the customer and associated products and media."],
|
||||
["56", "foundMedia", "Set `foundMedia` if the media's ID is equal to the ID passed as a route parameter."],
|
||||
["65", "!foundMedia", "If `foundMedia` isn't set, throw an error."],
|
||||
["72", "retrieveFile", "Retrieve the details of the media's file."]
|
||||
]
|
||||
|
||||
```ts title="src/api/store/customers/me/digital-products/[mediaId]/download/route.ts" highlights={downloadUrlHighlights} collapsibleLines="1-10" expandMoreLabel="Show Imports"
|
||||
@@ -2102,7 +2095,7 @@ import {
|
||||
} from "@medusajs/medusa"
|
||||
import {
|
||||
ModuleRegistrationName,
|
||||
remoteQueryObjectFromString,
|
||||
ContainerRegistrationKeys,
|
||||
MedusaError,
|
||||
} from "@medusajs/utils"
|
||||
|
||||
@@ -2113,9 +2106,9 @@ export const POST = async (
|
||||
const fileModuleService = req.scope.resolve(
|
||||
ModuleRegistrationName.FILE
|
||||
)
|
||||
const remoteQuery = req.scope.resolve("remoteQuery")
|
||||
const query = req.scope.resolve(ContainerRegistrationKeys.QUERY)
|
||||
|
||||
const customerQuery = remoteQueryObjectFromString({
|
||||
const { data: [customer] } = await query.graph({
|
||||
entryPoint: "customer",
|
||||
fields: [
|
||||
"orders.digital_product_order.*",
|
||||
@@ -2127,12 +2120,11 @@ export const POST = async (
|
||||
},
|
||||
})
|
||||
|
||||
const customerResult = await remoteQuery(customerQuery)
|
||||
const customerDigitalOrderIds = customerResult[0].orders
|
||||
const customerDigitalOrderIds = customer.orders
|
||||
.filter((order) => order.digital_product_order !== undefined)
|
||||
.map((order) => order.digital_product_order.id)
|
||||
|
||||
const dpoQuery = remoteQueryObjectFromString({
|
||||
const { data: dpoResult } = await query.graph({
|
||||
entryPoint: "digital_product_order",
|
||||
fields: [
|
||||
"products.medias.*",
|
||||
@@ -2144,8 +2136,6 @@ export const POST = async (
|
||||
},
|
||||
})
|
||||
|
||||
const dpoResult = await remoteQuery(dpoQuery)
|
||||
|
||||
if (!dpoResult.length) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.NOT_ALLOWED,
|
||||
|
||||
@@ -123,9 +123,9 @@ Create workflows to implement these flows, then utilize these workflows in other
|
||||
|
||||
## Manage Linked Records
|
||||
|
||||
If you've defined links between data models of two modules, you can manage them through two functions: remote link and remote query.
|
||||
If you've defined links between data models of two modules, you can manage them through two functions: remote link and Query.
|
||||
|
||||
Use the remote link to create a link between two records, and use the remote query to fetch data across linked data models.
|
||||
Use the remote link to create a link between two records, and use Query to fetch data across linked data models.
|
||||
|
||||
<CardList itemsPerRow={2} items={[
|
||||
{
|
||||
@@ -135,9 +135,9 @@ Use the remote link to create a link between two records, and use the remote que
|
||||
icon: AcademicCapSolid,
|
||||
},
|
||||
{
|
||||
href: "!docs!/advanced-development/modules/remote-query",
|
||||
title: "How to Use the Remote Query",
|
||||
text: "Learn how to fetch data across modules with remote query.",
|
||||
href: "!docs!/advanced-development/modules/query",
|
||||
title: "How to Use Query",
|
||||
text: "Learn how to fetch data across modules with Medusa's Query.",
|
||||
icon: AcademicCapSolid,
|
||||
},
|
||||
]} />
|
||||
|
||||
@@ -627,16 +627,16 @@ In the file `src/api/restaurants/route.ts` add the following API route:
|
||||
```ts title="src/api/restaurants/route.ts"
|
||||
// other imports...
|
||||
import { MedusaRequest, MedusaResponse } from "@medusajs/medusa"
|
||||
import { remoteQueryObjectFromString } from "@medusajs/utils"
|
||||
import { ContainerRegistrationKeys } from "@medusajs/utils"
|
||||
|
||||
// ...
|
||||
|
||||
export async function GET(req: MedusaRequest, res: MedusaResponse) {
|
||||
const { currency_code = "eur", ...queryFilters } = req.query
|
||||
|
||||
const remoteQuery = req.scope.resolve("remoteQuery")
|
||||
const query = req.scope.resolve(ContainerRegistrationKeys.QUERY)
|
||||
|
||||
const restaurantsQuery = remoteQueryObjectFromString({
|
||||
const { data: restaurants } = await query.graph({
|
||||
entryPoint: "restaurants",
|
||||
fields: [
|
||||
"id",
|
||||
@@ -662,13 +662,11 @@ export async function GET(req: MedusaRequest, res: MedusaResponse) {
|
||||
},
|
||||
})
|
||||
|
||||
const restaurants = await remoteQuery(restaurantsQuery)
|
||||
|
||||
return res.status(200).json({ restaurants })
|
||||
}
|
||||
```
|
||||
|
||||
This creates a `GET` API route at `/restaurants`. It uses remote query to retrieve a restaurant, its products, and the product variant’s prices for a specified currency.
|
||||
This creates a `GET` API route at `/restaurants`. It uses Query to retrieve a restaurant, its products, and the product variant’s prices for a specified currency.
|
||||
|
||||
### Test it Out
|
||||
|
||||
@@ -682,7 +680,7 @@ This returns the list of restaurants in the response.
|
||||
|
||||
### Further Reads
|
||||
|
||||
- [What is Remote Query and how to use it](!docs!/advanced-development/modules/remote-query)
|
||||
- [What is and how to use it](!docs!/advanced-development/modules/query)
|
||||
- [How to Retrieve Prices for Product Variants](../../../../commerce-modules/product/guides/price/page.mdx)
|
||||
|
||||
---
|
||||
@@ -1502,14 +1500,14 @@ Create the file `src/workflows/delivery/steps/notify-restaurant.ts` with the fol
|
||||
|
||||
export const notifyRestaurantStepHighlights = [
|
||||
["11", "async", "Set the step as async."],
|
||||
["18", "deliveryQuery", "Retrieve the delivery and its restaurant."],
|
||||
["32", "emit", "Emit a custom event that can be used to notify the restaurant that a new delivery is created."]
|
||||
["18", "graph", "Retrieve the delivery and its restaurant."],
|
||||
["30", "emit", "Emit a custom event that can be used to notify the restaurant that a new delivery is created."]
|
||||
]
|
||||
|
||||
```ts title="src/workflows/delivery/steps/notify-restaurant.ts" highlights={notifyRestaurantStepHighlights} collapsibleLines="1-6" expandButtonLabel="Show Imports"
|
||||
import {
|
||||
ModuleRegistrationName,
|
||||
remoteQueryObjectFromString,
|
||||
ContainerRegistrationKeys,
|
||||
} from "@medusajs/utils"
|
||||
import { createStep } from "@medusajs/workflows-sdk"
|
||||
|
||||
@@ -1522,9 +1520,9 @@ export const notifyRestaurantStep = createStep(
|
||||
maxRetries: 2,
|
||||
},
|
||||
async function (deliveryId: string, { container }) {
|
||||
const remoteQuery = container.resolve("remoteQuery")
|
||||
const query = container.resolve(ContainerRegistrationKeys.QUERY)
|
||||
|
||||
const deliveryQuery = remoteQueryObjectFromString({
|
||||
const { data: [delivery] } = await query.graph({
|
||||
entryPoint: "deliveries",
|
||||
variables: {
|
||||
filters: {
|
||||
@@ -1534,8 +1532,6 @@ export const notifyRestaurantStep = createStep(
|
||||
fields: ["id", "restaurant.id"],
|
||||
})
|
||||
|
||||
const delivery = await remoteQuery(deliveryQuery).then((res) => res[0])
|
||||
|
||||
const eventBus = container.resolve(ModuleRegistrationName.EVENT_BUS)
|
||||
|
||||
await eventBus.emit({
|
||||
@@ -1547,7 +1543,6 @@ export const notifyRestaurantStep = createStep(
|
||||
})
|
||||
}
|
||||
)
|
||||
|
||||
```
|
||||
|
||||
In this step, you:
|
||||
@@ -1600,7 +1595,7 @@ import { CreateOrderShippingMethodDTO } from "@medusajs/types"
|
||||
import {
|
||||
ModuleRegistrationName,
|
||||
Modules,
|
||||
remoteQueryObjectFromString,
|
||||
ContainerRegistrationKeys,
|
||||
} from "@medusajs/utils"
|
||||
import { StepResponse, createStep } from "@medusajs/workflows-sdk"
|
||||
import { DELIVERY_MODULE } from "../../../modules/delivery"
|
||||
@@ -1608,9 +1603,9 @@ import { DELIVERY_MODULE } from "../../../modules/delivery"
|
||||
export const createOrderStep = createStep(
|
||||
"create-order-step",
|
||||
async function (deliveryId: string, { container }) {
|
||||
const remoteQuery = container.resolve("remoteQuery")
|
||||
const query = container.resolve(ContainerRegistrationKeys.QUERY)
|
||||
|
||||
const deliveryQuery = remoteQueryObjectFromString({
|
||||
const { data: [delivery] } = await query.graph({
|
||||
entryPoint: "deliveries",
|
||||
variables: {
|
||||
filters: {
|
||||
@@ -1627,8 +1622,6 @@ export const createOrderStep = createStep(
|
||||
],
|
||||
})
|
||||
|
||||
const delivery = await remoteQuery(deliveryQuery).then((res) => res[0])
|
||||
|
||||
// TODO create order
|
||||
},
|
||||
async ({ orderId }, { container }) => {
|
||||
@@ -2409,8 +2402,8 @@ Start by creating the file `src/api/utils/is-delivery-restaurant.ts` with the fo
|
||||
|
||||
export const isDeliveryRestaurantHighlights = [
|
||||
["21", "restaurantAdmin", "Retrieve the logged-in restaurant admin."],
|
||||
["28", "query", "Retrieve the delivery based on the ID in the path parameter."],
|
||||
["42", "", "If the restaurant admin doesn't belong to the delivery's restaurant, return an unauthorized response."]
|
||||
["28", "graph", "Retrieve the delivery based on the ID in the path parameter."],
|
||||
["40", "", "If the restaurant admin doesn't belong to the delivery's restaurant, return an unauthorized response."]
|
||||
]
|
||||
|
||||
```ts title="src/api/utils/is-delivery-restaurant.ts" highlights={isDeliveryRestaurantHighlights} collapsibleLines="1-10" expandButtonLabel="Show Imports"
|
||||
@@ -2420,7 +2413,7 @@ import {
|
||||
MedusaResponse,
|
||||
} from "@medusajs/medusa"
|
||||
import {
|
||||
remoteQueryObjectFromString,
|
||||
ContainerRegistrationKeys,
|
||||
} from "@medusajs/utils"
|
||||
import { RESTAURANT_MODULE } from "../../modules/restaurant"
|
||||
|
||||
@@ -2429,7 +2422,7 @@ export const isDeliveryRestaurant = async (
|
||||
res: MedusaResponse,
|
||||
next: MedusaNextFunction
|
||||
) => {
|
||||
const remoteQuery = req.scope.resolve("remoteQuery")
|
||||
const query = req.scope.resolve(ContainerRegistrationKeys.QUERY)
|
||||
const restaurantModuleService = req.scope.resolve(
|
||||
RESTAURANT_MODULE
|
||||
)
|
||||
@@ -2441,7 +2434,7 @@ export const isDeliveryRestaurant = async (
|
||||
}
|
||||
)
|
||||
|
||||
const query = remoteQueryObjectFromString({
|
||||
const { data: [delivery] } = await query.graph({
|
||||
entryPoint: "delivery",
|
||||
fields: [
|
||||
"restaurant.*",
|
||||
@@ -2453,9 +2446,7 @@ export const isDeliveryRestaurant = async (
|
||||
},
|
||||
})
|
||||
|
||||
const result = await remoteQuery(query)
|
||||
|
||||
if (result[0].restaurant.id !== restaurantAdmin.restaurant.id) {
|
||||
if (delivery.restaurant.id !== restaurantAdmin.restaurant.id) {
|
||||
return res.status(403).json({
|
||||
message: "unauthorized",
|
||||
})
|
||||
|
||||
@@ -563,13 +563,13 @@ To create the API route that retrieves the vendor’s products, create the file
|
||||
|
||||
export const retrieveProductHighlights = [
|
||||
["16", "retrieveVendorAdmin", "Retrive the vendor admin to retrieve its vendor's ID."],
|
||||
["33", "remoteQuery", "Retrieve the vendor's products using remote query."]
|
||||
["23", "graph", "Retrieve the vendor's products using Query."]
|
||||
]
|
||||
|
||||
```ts title="src/api/vendors/products/route.ts" highlights={retrieveProductHighlights}
|
||||
import { AuthenticatedMedusaRequest, MedusaResponse } from "@medusajs/medusa"
|
||||
import {
|
||||
remoteQueryObjectFromString,
|
||||
ContainerRegistrationKeys,
|
||||
} from "@medusajs/utils"
|
||||
import MarketplaceModuleService from "../../../modules/marketplace/service"
|
||||
import { MARKETPLACE_MODULE } from "../../../modules/marketplace"
|
||||
@@ -578,7 +578,7 @@ export const GET = async (
|
||||
req: AuthenticatedMedusaRequest,
|
||||
res: MedusaResponse
|
||||
) => {
|
||||
const remoteQuery = req.scope.resolve("remoteQuery")
|
||||
const query = req.scope.resolve(ContainerRegistrationKeys.QUERY)
|
||||
const marketplaceModuleService: MarketplaceModuleService =
|
||||
req.scope.resolve(MARKETPLACE_MODULE)
|
||||
|
||||
@@ -589,7 +589,7 @@ export const GET = async (
|
||||
}
|
||||
)
|
||||
|
||||
const query = remoteQueryObjectFromString({
|
||||
const { data: [vendor] } = await query.graph({
|
||||
entryPoint: "vendor",
|
||||
fields: ["products.*"],
|
||||
variables: {
|
||||
@@ -599,15 +599,13 @@ export const GET = async (
|
||||
},
|
||||
})
|
||||
|
||||
const result = await remoteQuery(query)
|
||||
|
||||
res.json({
|
||||
products: result[0].products,
|
||||
products: vendor.products,
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
This adds a `GET` API route at `/vendors/products` that, using the remote query, retrieves the list of products of the vendor and returns them in the response.
|
||||
This adds a `GET` API route at `/vendors/products` that, using Query, retrieves the list of products of the vendor and returns them in the response.
|
||||
|
||||
To add the create product API route, add to the same file the following:
|
||||
|
||||
@@ -782,7 +780,7 @@ curl 'http://localhost:9000/vendors/products' \
|
||||
|
||||
### Further Reads
|
||||
|
||||
- [How to use the Remote Query](!docs!/advanced-development/modules/remote-query)
|
||||
- [How to use Query](!docs!/advanced-development/modules/query)
|
||||
- [How to use the Remote Link](!docs!/advanced-development/modules/remote-link)
|
||||
|
||||
---
|
||||
@@ -817,7 +815,7 @@ import {
|
||||
StepResponse,
|
||||
} from "@medusajs/workflows-sdk"
|
||||
import { CartDTO, CartLineItemDTO } from "@medusajs/types"
|
||||
import { remoteQueryObjectFromString } from "@medusajs/utils"
|
||||
import { ContainerRegistrationKeys } from "@medusajs/utils"
|
||||
|
||||
type StepInput = {
|
||||
cart: CartDTO
|
||||
@@ -826,12 +824,12 @@ type StepInput = {
|
||||
const groupVendorItemsStep = createStep(
|
||||
"group-vendor-items",
|
||||
async ({ cart }: StepInput, { container }) => {
|
||||
const remoteQuery = container.resolve("remoteQuery")
|
||||
const query = container.resolve(ContainerRegistrationKeys.QUERY)
|
||||
|
||||
const vendorsItems: Record<string, CartLineItemDTO[]> = {}
|
||||
|
||||
await Promise.all(cart.items?.map(async (item) => {
|
||||
const query = remoteQueryObjectFromString({
|
||||
const { data: [product] } = await query.graph({
|
||||
entryPoint: "product",
|
||||
fields: ["vendor.*"],
|
||||
variables: {
|
||||
@@ -841,9 +839,7 @@ const groupVendorItemsStep = createStep(
|
||||
},
|
||||
})
|
||||
|
||||
const result = await remoteQuery(query)
|
||||
|
||||
const vendorId = result[0].vendor?.id
|
||||
const vendorId = product.vendor?.id
|
||||
|
||||
if (!vendorId) {
|
||||
return
|
||||
@@ -1251,13 +1247,13 @@ Create the file `src/api/vendors/orders/route.ts` with the following content:
|
||||
|
||||
export const getOrderHighlights = [
|
||||
["15", "retrieveVendorAdmin", "Retrive the vendor admin to retrieve its vendor's ID."],
|
||||
["32", "remoteQuery", "Retrieve the orders of the vendor."],
|
||||
["34", "getOrdersListWorkflow", "Use Medusa's workflow to retrieve the list of orders."],
|
||||
["22", "graph", "Retrieve the orders of the vendor."],
|
||||
["32", "getOrdersListWorkflow", "Use Medusa's workflow to retrieve the list of orders."],
|
||||
]
|
||||
|
||||
```ts title="src/api/vendors/orders/route.ts" highlights={getOrderHighlights} collapsibleLines="1-6" expandMoreLabel="Show Imports"
|
||||
import { AuthenticatedMedusaRequest, MedusaResponse } from "@medusajs/medusa"
|
||||
import { remoteQueryObjectFromString } from "@medusajs/utils"
|
||||
import { ContainerRegistrationKeys } from "@medusajs/utils"
|
||||
import { getOrdersListWorkflow } from "@medusajs/core-flows"
|
||||
import MarketplaceModuleService from "../../../modules/marketplace/service"
|
||||
import { MARKETPLACE_MODULE } from "../../../modules/marketplace"
|
||||
@@ -1266,7 +1262,7 @@ export const GET = async (
|
||||
req: AuthenticatedMedusaRequest,
|
||||
res: MedusaResponse
|
||||
) => {
|
||||
const remoteQuery = req.scope.resolve("remoteQuery")
|
||||
const query = req.scope.resolve(ContainerRegistrationKeys.QUERY)
|
||||
const marketplaceModuleService: MarketplaceModuleService =
|
||||
req.scope.resolve(MARKETPLACE_MODULE)
|
||||
|
||||
@@ -1277,7 +1273,7 @@ export const GET = async (
|
||||
}
|
||||
)
|
||||
|
||||
const query = remoteQueryObjectFromString({
|
||||
const { data: [vendor] } = await query.graph({
|
||||
entryPoint: "vendor",
|
||||
fields: ["orders.*"],
|
||||
variables: {
|
||||
@@ -1287,8 +1283,6 @@ export const GET = async (
|
||||
},
|
||||
})
|
||||
|
||||
const result = await remoteQuery(query)
|
||||
|
||||
const { result: orders } = await getOrdersListWorkflow(req.scope)
|
||||
.run({
|
||||
input: {
|
||||
@@ -1310,7 +1304,7 @@ export const GET = async (
|
||||
],
|
||||
variables: {
|
||||
filters: {
|
||||
id: result[0].orders.map((order) => order.id),
|
||||
id: vendor.orders.map((order) => order.id),
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -654,7 +654,7 @@ In this step, you’ll change what happens when the [Complete Cart API route](!a
|
||||
Create the file `src/api/store/carts/[id]/complete/route.ts` with the following content:
|
||||
|
||||
export const completeCartHighlights = [
|
||||
["17", "remoteQueryObjectFromString", "Retrieve the cart to retrieve the subscription details from the `metadata`."],
|
||||
["17", "graph", "Retrieve the cart to retrieve the subscription details from the `metadata`."],
|
||||
["31", "", "If the subscription data isn't set in the cart's `metadata`, throw an error"],
|
||||
["38", "createSubscriptionWorkflow", "Execute the workflow created in the previous step."]
|
||||
]
|
||||
@@ -665,7 +665,7 @@ import {
|
||||
MedusaResponse,
|
||||
} from "@medusajs/medusa"
|
||||
import {
|
||||
remoteQueryObjectFromString,
|
||||
ContainerRegistrationKeys,
|
||||
MedusaError,
|
||||
} from "@medusajs/utils"
|
||||
import createSubscriptionWorkflow from "../../../../../workflows/create-subscription"
|
||||
@@ -674,9 +674,9 @@ export const POST = async (
|
||||
req: MedusaRequest,
|
||||
res: MedusaResponse
|
||||
) => {
|
||||
const remoteQuery = req.scope.resolve("remoteQuery")
|
||||
const query = req.scope.resolve(ContainerRegistrationKeys.QUERY)
|
||||
|
||||
const query = remoteQueryObjectFromString({
|
||||
const { data: [cart] } = await query.graph({
|
||||
entryPoint: "cart",
|
||||
fields: [
|
||||
"metadata",
|
||||
@@ -688,7 +688,7 @@ export const POST = async (
|
||||
},
|
||||
})
|
||||
|
||||
const { metadata } = (await remoteQuery(query))[0]
|
||||
const { metadata } = cart
|
||||
|
||||
if (!metadata?.subscription_interval || !metadata.subscription_period) {
|
||||
throw new MedusaError(
|
||||
@@ -973,7 +973,7 @@ In this step, you’ll add two API routes for admin users:
|
||||
Create the file `src/api/admin/subscriptions/route.ts` with the following content:
|
||||
|
||||
export const listSubscriptionsAdminHighlight = [
|
||||
["18", "remoteQueryObjectFromString", "Retrieve the subscriptions with their orders and customer."]
|
||||
["21", "graph", "Retrieve the subscriptions with their orders and customer."]
|
||||
]
|
||||
|
||||
```ts title="src/api/admin/subscriptions/route.ts" highlights={listSubscriptionsAdminHighlight}
|
||||
@@ -981,20 +981,23 @@ import {
|
||||
AuthenticatedMedusaRequest,
|
||||
MedusaResponse,
|
||||
} from "@medusajs/medusa"
|
||||
import { remoteQueryObjectFromString } from "@medusajs/utils"
|
||||
import { ContainerRegistrationKeys } from "@medusajs/utils"
|
||||
|
||||
export const GET = async (
|
||||
req: AuthenticatedMedusaRequest,
|
||||
res: MedusaResponse
|
||||
) => {
|
||||
const remoteQuery = req.scope.resolve("remoteQuery")
|
||||
const query = req.scope.resolve(ContainerRegistrationKeys.QUERY)
|
||||
|
||||
const {
|
||||
limit = 20,
|
||||
offset = 0,
|
||||
} = req.validatedQuery || {}
|
||||
|
||||
const query = remoteQueryObjectFromString({
|
||||
const {
|
||||
data: subscriptions,
|
||||
metadata: { count, take, skip },
|
||||
} = await query.graph({
|
||||
entryPoint: "subscription",
|
||||
fields: [
|
||||
"*",
|
||||
@@ -1011,13 +1014,8 @@ export const GET = async (
|
||||
},
|
||||
})
|
||||
|
||||
const {
|
||||
rows,
|
||||
metadata: { count, take, skip },
|
||||
} = await remoteQuery(query)
|
||||
|
||||
res.json({
|
||||
subscriptions: rows,
|
||||
subscriptions,
|
||||
count,
|
||||
limit: take,
|
||||
offset: skip,
|
||||
@@ -1027,7 +1025,7 @@ export const GET = async (
|
||||
|
||||
This adds a `GET` API route at `/admin/subscriptions`.
|
||||
|
||||
In the route handler, you use the remote query to retrieve a subscription with its orders and customer.
|
||||
In the route handler, you use Query to retrieve a subscription with its orders and customer.
|
||||
|
||||
The API route accepts pagination parameters to paginate the subscription list. It returns the subscriptions with pagination parameters in the response.
|
||||
|
||||
@@ -1036,7 +1034,7 @@ The API route accepts pagination parameters to paginate the subscription list. I
|
||||
Create the file `src/api/admin/subscriptions/[id]/route.ts` with the following content:
|
||||
|
||||
export const getSubscriptionsAdminHighlight = [
|
||||
["13", "remoteQueryObjectFromString", "Retrieve the subscription with its orders and customer."]
|
||||
["13", "graph", "Retrieve the subscription with its orders and customer."]
|
||||
]
|
||||
|
||||
```ts title="src/api/admin/subscriptions/[id]/route.ts" highlights={getSubscriptionsAdminHighlight}
|
||||
@@ -1044,15 +1042,15 @@ import {
|
||||
AuthenticatedMedusaRequest,
|
||||
MedusaResponse,
|
||||
} from "@medusajs/medusa"
|
||||
import { remoteQueryObjectFromString } from "@medusajs/utils"
|
||||
import { ContainerRegistrationKeys } from "@medusajs/utils"
|
||||
|
||||
export const GET = async (
|
||||
req: AuthenticatedMedusaRequest,
|
||||
res: MedusaResponse
|
||||
) => {
|
||||
const remoteQuery = req.scope.resolve("remoteQuery")
|
||||
const query = req.scope.resolve(ContainerRegistrationKeys.QUERY)
|
||||
|
||||
const query = remoteQueryObjectFromString({
|
||||
const { data: [subscription] } = await query.graph({
|
||||
entryPoint: "subscription",
|
||||
fields: [
|
||||
"*",
|
||||
@@ -1067,17 +1065,15 @@ export const GET = async (
|
||||
},
|
||||
})
|
||||
|
||||
const result = await remoteQuery(query)
|
||||
|
||||
res.json({
|
||||
subscription: result[0],
|
||||
subscription,
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
This adds a `GET` API route at `/admin/subscriptions/[id]`, where `[id]` is the ID of the subscription to retrieve.
|
||||
|
||||
In the route handler, you retrieve a subscription by its ID using the remote query and return it in the response.
|
||||
In the route handler, you retrieve a subscription by its ID using Query and return it in the response.
|
||||
|
||||
In the next section, you’ll extend the Medusa admin and use these API routes to show the subscriptions.
|
||||
|
||||
@@ -2130,15 +2126,15 @@ import {
|
||||
AuthenticatedMedusaRequest,
|
||||
MedusaResponse,
|
||||
} from "@medusajs/medusa"
|
||||
import { remoteQueryObjectFromString } from "@medusajs/utils"
|
||||
import { ContainerRegistrationKeys } from "@medusajs/utils"
|
||||
|
||||
export const GET = async (
|
||||
req: AuthenticatedMedusaRequest,
|
||||
res: MedusaResponse
|
||||
) => {
|
||||
const remoteQuery = req.scope.resolve("remoteQuery")
|
||||
const query = req.scope.resolve(ContainerRegistrationKeys.QUERY)
|
||||
|
||||
const query = remoteQueryObjectFromString({
|
||||
const { data: [customer] } = await query.graph({
|
||||
entryPoint: "customer",
|
||||
fields: [
|
||||
"subscriptions.*",
|
||||
@@ -2150,17 +2146,15 @@ export const GET = async (
|
||||
},
|
||||
})
|
||||
|
||||
const result = await remoteQuery(query)
|
||||
|
||||
res.json({
|
||||
subscriptions: result[0].subscriptions,
|
||||
subscriptions: customer.subscriptions,
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
This adds an API route at `/store/customers/me/subscriptions`.
|
||||
|
||||
In the route handler, you retrieve the authenticated customer’s subscriptions using the remote query and return them in the response.
|
||||
In the route handler, you retrieve the authenticated customer’s subscriptions using Query and return them in the response.
|
||||
|
||||
### Cancel Subscription API Route
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@ The method returns an array of the first `15` records matching the filters.
|
||||
|
||||
<Note>
|
||||
|
||||
This applies to relations between data models of the same module. To retrieve linked records of different modules, use [remote query](!docs!/advanced-development/modules/remote-query).
|
||||
This applies to relations between data models of the same module. To retrieve linked records of different modules, use [Query](!docs!/advanced-development/modules/query).
|
||||
|
||||
</Note>
|
||||
|
||||
|
||||
@@ -54,7 +54,7 @@ The method returns an array with two items:
|
||||
|
||||
<Note>
|
||||
|
||||
This applies to relations between data models of the same module. To retrieve linked records of different modules, use [remote query](!docs!/advanced-development/modules/remote-query).
|
||||
This applies to relations between data models of the same module. To retrieve linked records of different modules, use [Query](!docs!/advanced-development/modules/query).
|
||||
|
||||
</Note>
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ The method returns the record as an object.
|
||||
|
||||
<Note>
|
||||
|
||||
This applies to relations between data models of the same module. To retrieve linked records of different modules, use [remote query](!docs!/advanced-development/modules/remote-query).
|
||||
This applies to relations between data models of the same module. To retrieve linked records of different modules, use [Query](!docs!/advanced-development/modules/query).
|
||||
|
||||
</Note>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user