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:
Shahed Nasser
2024-09-10 12:31:47 +00:00
committed by GitHub
parent 1d8dd54014
commit e9b5f76f9a
31 changed files with 437 additions and 635 deletions
@@ -17,7 +17,7 @@ export const arrowHighlights = [
```ts highlights={arrowHighlights} ```ts highlights={arrowHighlights}
// Don't // Don't
function ProductWidget () { function ProductWidget() {
// ... // ...
} }
@@ -31,7 +31,7 @@ const ProductWidget = () => {
} }
fetch(`/admin/products`, { fetch(`/admin/products`, {
credentials: "include" credentials: "include",
}) })
.then((res) => res.json()) .then((res) => res.json())
.then(({ count }) => { .then(({ count }) => {
@@ -256,7 +256,7 @@ import {
defineMiddlewares, defineMiddlewares,
MedusaNextFunction, MedusaNextFunction,
MedusaRequest, MedusaRequest,
MedusaResponse MedusaResponse,
} from "@medusajs/medusa" } from "@medusajs/medusa"
import { MedusaError } from "@medusajs/utils" import { MedusaError } from "@medusajs/utils"
@@ -268,9 +268,9 @@ export default defineMiddlewares({
next: MedusaNextFunction next: MedusaNextFunction
) => { ) => {
res.status(400).json({ 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"]]} ```ts title="src/api/admin/custom/route.ts" highlights={[["15"]]}
import type { import type {
AuthenticatedMedusaRequest, AuthenticatedMedusaRequest,
MedusaResponse MedusaResponse,
} from "@medusajs/medusa" } from "@medusajs/medusa"
export const GET = async ( 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" ```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 ( export const GET = async (
req: MedusaRequest, req: MedusaRequest,
res: MedusaResponse res: MedusaResponse
) => { ) => {
res.json({ 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" ```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 ( export const GET = async (
req: MedusaRequest, req: MedusaRequest,
res: MedusaResponse res: MedusaResponse
) => { ) => {
res.status(201).json({ res.status(201).json({
message: "Hello, World!" message: "Hello, World!",
}) })
} }
``` ```
@@ -84,7 +84,7 @@ export const streamHighlights = [
] ]
```ts highlights={streamHighlights} ```ts highlights={streamHighlights}
import { MedusaRequest, MedusaResponse } from "@medusajs/medusa"; import { MedusaRequest, MedusaResponse } from "@medusajs/medusa"
export const GET = async ( export const GET = async (
req: MedusaRequest, req: MedusaRequest,
@@ -27,7 +27,7 @@ import { z } from "zod"
export const PostStoreCustomSchema = z.object({ export const PostStoreCustomSchema = z.object({
a: z.number(), 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" ```ts title="src/api/middlewares.ts"
import { defineMiddlewares } from "@medusajs/medusa" import { defineMiddlewares } from "@medusajs/medusa"
import { import {
validateAndTransformBody validateAndTransformBody,
} from "@medusajs/medusa/dist/api/utils/validate-body" } from "@medusajs/medusa/dist/api/utils/validate-body"
import { PostStoreCustomSchema } from "./store/custom/validators" import { PostStoreCustomSchema } from "./store/custom/validators"
@@ -58,7 +58,7 @@ export default defineMiddlewares({
matcher: "/store/custom", matcher: "/store/custom",
method: "POST", method: "POST",
middlewares: [ middlewares: [
validateAndTransformBody(PostStoreCustomSchema) validateAndTransformBody(PostStoreCustomSchema),
], ],
}, },
], ],
@@ -87,9 +87,9 @@ export const routeHighlights = [
] ]
```ts title="src/api/store/custom/route.ts" highlights={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 { z } from "zod"
import { PostStoreCustomSchema } from "./validators"; import { PostStoreCustomSchema } from "./validators"
type PostStoreCustomSchemaType = z.infer< type PostStoreCustomSchemaType = z.infer<
typeof PostStoreCustomSchema typeof PostStoreCustomSchema
@@ -100,7 +100,7 @@ export const POST = async (
res: MedusaResponse res: MedusaResponse
) => { ) => {
res.json({ 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( const product = await helloModuleService.retrieveProduct(
"123", "123",
{ {
relations: ["orders"] relations: ["orders"],
} }
) )
``` ```
@@ -22,10 +22,10 @@ export const highlights = [
```ts highlights={highlights} ```ts highlights={highlights}
import { import {
createWorkflow createWorkflow,
} from "@medusajs/workflows-sdk" } from "@medusajs/workflows-sdk"
import { import {
emitEventStep emitEventStep,
} from "@medusajs/core-flows" } from "@medusajs/core-flows"
const helloWorldWorkflow = createWorkflow( const helloWorldWorkflow = createWorkflow(
@@ -36,8 +36,8 @@ const helloWorldWorkflow = createWorkflow(
emitEventStep({ emitEventStep({
eventName: "custom.created", eventName: "custom.created",
data: { data: {
id: "123" id: "123",
} },
}) })
} }
) )
@@ -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, youll 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. Its 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 Medusas 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 models 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 models 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, youll 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. Its 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 Medusas 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 models 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 models 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 objects key is the propertys 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: 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"]]} ```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" import { createProductsWorkflow } from "@medusajs/core-flows"
export async function POST(req: MedusaRequest, res: MedusaResponse) { export async function POST(req: MedusaRequest, res: MedusaResponse) {
@@ -104,8 +104,8 @@ export async function POST(req: MedusaRequest, res: MedusaResponse) {
// ... // ...
], ],
additional_data: { additional_data: {
custom_field: "test" custom_field: "test",
} },
}, },
}) })
} }
@@ -29,7 +29,7 @@ export async function GET(
res: MedusaResponse res: MedusaResponse
){ ){
res.json({ 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" ```ts title="src/api/store/custom/route.ts"
// other imports... // other imports...
import HelloModuleService from "../../../modules/hello/service"; import HelloModuleService from "../../../modules/hello/service"
// ... // ...
@@ -124,7 +124,7 @@ export async function POST(
) )
res.json({ res.json({
my_custom: myCustom my_custom: myCustom,
}) })
} }
``` ```
@@ -155,7 +155,7 @@ medusaIntegrationTestRunner({
`/store/custom`, `/store/custom`,
{ {
id, 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`: 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" ```ts title="src/api/store/custom/[id]/route.ts"
import { MedusaRequest, MedusaResponse } from "@medusajs/medusa"; import { MedusaRequest, MedusaResponse } from "@medusajs/medusa"
import HelloModuleService from "../../../modules/hello/service"; import HelloModuleService from "../../../modules/hello/service"
export async function DELETE( export async function DELETE(
req: MedusaRequest, req: MedusaRequest,
@@ -237,7 +237,7 @@ export async function DELETE(
await helloModuleService.deleteMyCustoms(req.params.id) await helloModuleService.deleteMyCustoms(req.params.id)
res.json({ res.json({
success: true success: true,
}) })
} }
``` ```
@@ -266,7 +266,7 @@ medusaIntegrationTestRunner({
await helloModuleService.createMyCustoms({ await helloModuleService.createMyCustoms({
id, id,
name: "Test" name: "Test",
}) })
}) })
@@ -281,7 +281,7 @@ medusaIntegrationTestRunner({
}) })
}) })
}) })
} },
}) })
``` ```
@@ -34,7 +34,7 @@ import { medusaIntegrationTestRunner } from "medusa-test-utils"
medusaIntegrationTestRunner({ medusaIntegrationTestRunner({
testSuite: ({ api, getContainer }) => { testSuite: ({ api, getContainer }) => {
// TODO write tests... // TODO write tests...
} },
}) })
``` ```
@@ -26,7 +26,7 @@ import {
createWorkflow, createWorkflow,
createStep, createStep,
StepResponse, StepResponse,
WorkflowResponse WorkflowResponse,
} from "@medusajs/workflows-sdk" } from "@medusajs/workflows-sdk"
const step1 = createStep("step-1", () => { const step1 = createStep("step-1", () => {
@@ -59,7 +59,7 @@ medusaIntegrationTestRunner({
expect(result).toEqual("Hello, World!") expect(result).toEqual("Hello, World!")
}) })
}) })
} },
}) })
``` ```
@@ -56,7 +56,7 @@ moduleIntegrationTestRunner<HelloModuleService>({
expect(message).toEqual("Hello, World!") expect(message).toEqual("Hello, World!")
}) })
}) })
} },
}) })
``` ```
@@ -35,7 +35,7 @@ moduleIntegrationTestRunner<HelloModuleService>({
resolve: "./modules/hello", resolve: "./modules/hello",
testSuite: ({ service }) => { testSuite: ({ service }) => {
// TODO write tests // TODO write tests
} },
}) })
``` ```
@@ -88,7 +88,7 @@ import HelloModuleService from "../service"
moduleIntegrationTestRunner<HelloModuleService>({ moduleIntegrationTestRunner<HelloModuleService>({
moduleOptions: { moduleOptions: {
apiKey: "123" apiKey: "123",
}, },
// ... // ...
}) })
@@ -108,7 +108,7 @@ import HelloModuleService from "../service"
import { model } from "@medusajs/utils" import { model } from "@medusajs/utils"
const DummyModel = model.define("dummy_model", { const DummyModel = model.define("dummy_model", {
id: model.id().primaryKey() id: model.id().primaryKey(),
}) })
moduleIntegrationTestRunner<HelloModuleService>({ 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: Then, create the file `jest.config.js` with the following content:
```js title="jest.config.js" ```js title="jest.config.js"
const { loadEnv } = require('@medusajs/utils') const { loadEnv } = require("@medusajs/utils")
loadEnv('test', process.cwd()) loadEnv("test", process.cwd())
module.exports = { module.exports = {
transform: { transform: {
+16 -12
View File
@@ -6,7 +6,7 @@ export const generatedEditDates = {
"app/basics/modules-and-services/page.mdx": "2024-09-03T07:45:28.079Z", "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/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/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/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/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", "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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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", "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/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/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/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/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/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", "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/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/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/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/api-routes/page.mdx": "2024-09-10T11:39:51.170Z",
"app/debugging-and-testing/testing-tools/integration-tests/page.mdx": "2024-09-02T10:56:09.872Z", "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-02T10:57:04.202Z", "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-02T10:08:29.388Z", "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/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/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/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/responses/page.mdx": "2024-09-10T11:39:51.167Z",
"app/advanced-development/api-routes/validation/page.mdx": "2024-09-04T09:50:52.129Z", "app/advanced-development/api-routes/validation/page.mdx": "2024-09-10T11:39:51.167Z",
"app/advanced-development/api-routes/errors/page.mdx": "2024-09-04T11:03:55.017Z" "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"
} }
+9
View File
@@ -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) export default withMDX(nextConfig)
+2 -2
View File
@@ -172,8 +172,8 @@ export const sidebar = numberSidebarItems(
}, },
{ {
type: "link", type: "link",
path: "/advanced-development/modules/remote-query", path: "/advanced-development/modules/query",
title: "Remote Query", title: "Query",
}, },
{ {
type: "link", 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: 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. 2. The Tax Module's main service to get the tax lines for each product.
```ts ```ts
// other imports... // other imports...
import { import {
ModuleRegistrationName, ModuleRegistrationName,
ContainerRegistrationKeys,
} from "@medusajs/utils" } from "@medusajs/utils"
// In an API route, workflow step, etc... // In an API route, workflow step, etc...
const remoteQuery = container.resolve("remoteQuery") const query = container.resolve(ContainerRegistrationKeys.QUERY)
const taxModuleService = container.resolve( const taxModuleService = container.resolve(
ModuleRegistrationName.TAX ModuleRegistrationName.TAX
) )
@@ -34,16 +35,10 @@ const taxModuleService = container.resolve(
## Step 1: Retrieve Prices for a Context ## 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 ```ts
// other imports... const { data: products } = await query.graph({
import {
remoteQueryObjectFromString,
} from "@medusajs/utils"
// ...
const query = remoteQueryObjectFromString({
entryPoint: "product", entryPoint: "product",
fields: [ fields: [
"*", "*",
@@ -52,18 +47,16 @@ const query = remoteQueryObjectFromString({
], ],
variables: { variables: {
filters: { filters: {
id, id: "prod_123",
}, },
"variants.calculated_price": { "variants.calculated_price": {
context: { context: {
region_id, region_id: "region_123",
currency_code, currency_code: "usd",
}, },
}, },
}, },
}) })
const products = await remoteQuery(query)
``` ```
<Note> <Note>
@@ -3,29 +3,29 @@ sidebar_label: "Get Product Variant Prices"
--- ---
export const metadata = { export const metadata = {
title: `Get Product Variant Prices using Remote Query`, title: `Get Product Variant Prices using Query`,
} }
# {metadata.title} # {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. 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> </Note>
## Retrieve All Product Variant Prices ## 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: For example:
```ts highlights={[["6"]]} ```ts highlights={[["6"]]}
const query = remoteQueryObjectFromString({ const { data: products } = await query.graph({
entryPoint: "product", entryPoint: "product",
fields: [ 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). 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> </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 `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). - 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: For example:
```ts highlights={[["6"], ["12"], ["13"], ["14"], ["15"], ["16"], ["17"]]} ```ts highlights={[["6"], ["12"], ["13"], ["14"], ["15"], ["16"], ["17"]]}
const query = remoteQueryObjectFromString({ const { data: products } = await query.graph({
entryPoint: "product", entryPoint: "product",
fields: [ fields: [
"*", "*",
@@ -76,7 +73,7 @@ const query = remoteQueryObjectFromString({
], ],
variables: { variables: {
filters: { filters: {
id, id: "prod_123",
}, },
"variants.calculated_price": { "variants.calculated_price": {
context: { 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. 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. 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>
<Table.Header> <Table.Header>
<Table.Row> <Table.Row>
@@ -43,7 +49,7 @@ This documentation page includes the list of resources registered in the Medusa
</Table.Cell> </Table.Cell>
<Table.Cell> <Table.Cell>
`configModule` `configModule` or `ContainerRegistrationKeys.CONFIG_MODULE`
</Table.Cell> </Table.Cell>
</Table.Row> </Table.Row>
@@ -61,7 +67,7 @@ This documentation page includes the list of resources registered in the Medusa
</Table.Cell> </Table.Cell>
<Table.Cell> <Table.Cell>
`logger` `logger` or `ContainerRegistrationKeys.LOGGER`
</Table.Cell> </Table.Cell>
</Table.Row> </Table.Row>
@@ -69,17 +75,17 @@ This documentation page includes the list of resources registered in the Medusa
<Table.Row> <Table.Row>
<Table.Cell> <Table.Cell>
Remote Query Query
</Table.Cell> </Table.Cell>
<Table.Cell> <Table.Cell>
The remote query function. The Query utility methods.
</Table.Cell> </Table.Cell>
<Table.Cell> <Table.Cell>
`remoteQuery` `query` or `ContainerRegistrationKeys.QUERY`
</Table.Cell> </Table.Cell>
</Table.Row> </Table.Row>
@@ -97,7 +103,7 @@ This documentation page includes the list of resources registered in the Medusa
</Table.Cell> </Table.Cell>
<Table.Cell> <Table.Cell>
`remoteLink` `remoteLink` or `ContainerRegistrationKeys.REMOTE_LINK`
</Table.Cell> </Table.Cell>
</Table.Row> </Table.Row>
@@ -288,7 +288,7 @@ import {
AuthenticatedMedusaRequest, AuthenticatedMedusaRequest,
MedusaResponse, MedusaResponse,
} from "@medusajs/medusa" } from "@medusajs/medusa"
import { remoteQueryObjectFromString } from "@medusajs/utils" import { ContainerRegistrationKeys } from "@medusajs/utils"
export const GET = async ( export const GET = async (
req: AuthenticatedMedusaRequest, req: AuthenticatedMedusaRequest,
@@ -299,9 +299,12 @@ export const GET = async (
limit = 20, limit = 20,
offset = 0, offset = 0,
} = req.validatedQuery || {} } = 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", entryPoint: "digital_product",
fields: [ fields: [
"*", "*",
@@ -315,13 +318,8 @@ export const GET = async (
}, },
}) })
const {
rows,
metadata: { count, take, skip },
} = await remoteQuery(query)
res.json({ res.json({
digital_products: rows, digital_products: digitalProducts,
count, count,
limit: take, limit: take,
offset: skip, offset: skip,
@@ -331,7 +329,7 @@ export const GET = async (
This adds a `GET` API route at `/admin/digital-products`. 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 ### Test API Route
@@ -364,7 +362,7 @@ Make sure to replace `{token}` with the JWT token you retrieved.
### Further Reads ### Further Reads
- [How to Create an API Route](!docs!/basics/api-routes) - [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 = [ export const subscriberHighlight = [
["20", "notificationModuleService", "Resolve the Notification Module's service to use it later to send a notification."], ["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."], ["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" ```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" } from "@medusajs/types"
import { import {
ModuleRegistrationName, ModuleRegistrationName,
remoteQueryObjectFromString, ContainerRegistrationKeys,
} from "@medusajs/utils" } from "@medusajs/utils"
import { MediaType } from "../modules/digital-product/types" import { MediaType } from "../modules/digital-product/types"
@@ -1852,14 +1850,14 @@ async function digitalProductOrderCreatedHandler({
event: { data }, event: { data },
container, container,
}: SubscriberArgs<{ id: string }>) { }: SubscriberArgs<{ id: string }>) {
const remoteQuery = container.resolve("remoteQuery") const query = container.resolve(ContainerRegistrationKeys.QUERY)
const notificationModuleService: INotificationModuleService = container const notificationModuleService: INotificationModuleService = container
.resolve(ModuleRegistrationName.NOTIFICATION) .resolve(ModuleRegistrationName.NOTIFICATION)
const fileModuleService: IFileModuleService = container.resolve( const fileModuleService: IFileModuleService = container.resolve(
ModuleRegistrationName.FILE ModuleRegistrationName.FILE
) )
const query = remoteQueryObjectFromString({ const { data: [digitalProductOrder] } = await query.graph({
entryPoint: "digital_product_order", entryPoint: "digital_product_order",
fields: [ fields: [
"*", "*",
@@ -1874,8 +1872,6 @@ async function digitalProductOrderCreatedHandler({
}, },
}) })
const digitalProductOrder = (await remoteQuery(query))[0]
// TODO format and send notification // 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: Create the file `src/api/store/customers/me/digital-products/route.ts` with the following content:
export const purchasedDpHighlights = [ 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" ```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, MedusaResponse,
} from "@medusajs/medusa" } from "@medusajs/medusa"
import { import {
remoteQueryObjectFromString, ContainerRegistrationKeys,
} from "@medusajs/utils" } from "@medusajs/utils"
export const GET = async ( export const GET = async (
req: AuthenticatedMedusaRequest, req: AuthenticatedMedusaRequest,
res: MedusaResponse 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", entryPoint: "customer",
fields: [ fields: [
"orders.digital_product_order.products.*", "orders.digital_product_order.products.*",
@@ -2062,11 +2058,9 @@ export const GET = async (
}, },
}) })
const result = await remoteQuery(query)
const digitalProducts = {} const digitalProducts = {}
result[0].orders.forEach((order) => { customer.orders.forEach((order) => {
order.digital_product_order.products.forEach((product) => { order.digital_product_order.products.forEach((product) => {
digitalProducts[product.id] = product digitalProducts[product.id] = product
}) })
@@ -2076,23 +2070,22 @@ export const GET = async (
digital_products: Object.values(digitalProducts), 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. 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 customers 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 customers orders and linked digital product orders, and return the purchased digital products in the response.
### Get Digital Product Media Download URL API Route ### 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: Create the file `src/api/store/customers/me/digital-products/[mediaId]/download/route.ts` with the following content:
export const downloadUrlHighlights = [ export const downloadUrlHighlights = [
["20", "remoteQueryObjectFromString", "Get the customer's orders and linked digital orders."], ["20", "query.graph", "Get the customer's orders and linked digital orders."],
["37", "remoteQueryObjectFromString", "Get the digital product orders of the customer and associated products and media."], ["36", "query.graph", "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."], ["56", "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."], ["65", "!foundMedia", "If `foundMedia` isn't set, throw an error."],
["75", "retrieveFile", "Retrieve the details of the media's file."] ["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" ```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" } from "@medusajs/medusa"
import { import {
ModuleRegistrationName, ModuleRegistrationName,
remoteQueryObjectFromString, ContainerRegistrationKeys,
MedusaError, MedusaError,
} from "@medusajs/utils" } from "@medusajs/utils"
@@ -2113,9 +2106,9 @@ export const POST = async (
const fileModuleService = req.scope.resolve( const fileModuleService = req.scope.resolve(
ModuleRegistrationName.FILE 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", entryPoint: "customer",
fields: [ fields: [
"orders.digital_product_order.*", "orders.digital_product_order.*",
@@ -2127,12 +2120,11 @@ export const POST = async (
}, },
}) })
const customerResult = await remoteQuery(customerQuery) const customerDigitalOrderIds = customer.orders
const customerDigitalOrderIds = customerResult[0].orders
.filter((order) => order.digital_product_order !== undefined) .filter((order) => order.digital_product_order !== undefined)
.map((order) => order.digital_product_order.id) .map((order) => order.digital_product_order.id)
const dpoQuery = remoteQueryObjectFromString({ const { data: dpoResult } = await query.graph({
entryPoint: "digital_product_order", entryPoint: "digital_product_order",
fields: [ fields: [
"products.medias.*", "products.medias.*",
@@ -2144,8 +2136,6 @@ export const POST = async (
}, },
}) })
const dpoResult = await remoteQuery(dpoQuery)
if (!dpoResult.length) { if (!dpoResult.length) {
throw new MedusaError( throw new MedusaError(
MedusaError.Types.NOT_ALLOWED, MedusaError.Types.NOT_ALLOWED,
@@ -123,9 +123,9 @@ Create workflows to implement these flows, then utilize these workflows in other
## Manage Linked Records ## 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={[ <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, icon: AcademicCapSolid,
}, },
{ {
href: "!docs!/advanced-development/modules/remote-query", href: "!docs!/advanced-development/modules/query",
title: "How to Use the Remote Query", title: "How to Use Query",
text: "Learn how to fetch data across modules with remote query.", text: "Learn how to fetch data across modules with Medusa's Query.",
icon: AcademicCapSolid, 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" ```ts title="src/api/restaurants/route.ts"
// other imports... // other imports...
import { MedusaRequest, MedusaResponse } from "@medusajs/medusa" import { MedusaRequest, MedusaResponse } from "@medusajs/medusa"
import { remoteQueryObjectFromString } from "@medusajs/utils" import { ContainerRegistrationKeys } from "@medusajs/utils"
// ... // ...
export async function GET(req: MedusaRequest, res: MedusaResponse) { export async function GET(req: MedusaRequest, res: MedusaResponse) {
const { currency_code = "eur", ...queryFilters } = req.query 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", entryPoint: "restaurants",
fields: [ fields: [
"id", "id",
@@ -662,13 +662,11 @@ export async function GET(req: MedusaRequest, res: MedusaResponse) {
}, },
}) })
const restaurants = await remoteQuery(restaurantsQuery)
return res.status(200).json({ restaurants }) 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 variants 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 variants prices for a specified currency.
### Test it Out ### Test it Out
@@ -682,7 +680,7 @@ This returns the list of restaurants in the response.
### Further Reads ### 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) - [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 = [ export const notifyRestaurantStepHighlights = [
["11", "async", "Set the step as async."], ["11", "async", "Set the step as async."],
["18", "deliveryQuery", "Retrieve the delivery and its restaurant."], ["18", "graph", "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."] ["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" ```ts title="src/workflows/delivery/steps/notify-restaurant.ts" highlights={notifyRestaurantStepHighlights} collapsibleLines="1-6" expandButtonLabel="Show Imports"
import { import {
ModuleRegistrationName, ModuleRegistrationName,
remoteQueryObjectFromString, ContainerRegistrationKeys,
} from "@medusajs/utils" } from "@medusajs/utils"
import { createStep } from "@medusajs/workflows-sdk" import { createStep } from "@medusajs/workflows-sdk"
@@ -1522,9 +1520,9 @@ export const notifyRestaurantStep = createStep(
maxRetries: 2, maxRetries: 2,
}, },
async function (deliveryId: string, { container }) { 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", entryPoint: "deliveries",
variables: { variables: {
filters: { filters: {
@@ -1534,8 +1532,6 @@ export const notifyRestaurantStep = createStep(
fields: ["id", "restaurant.id"], fields: ["id", "restaurant.id"],
}) })
const delivery = await remoteQuery(deliveryQuery).then((res) => res[0])
const eventBus = container.resolve(ModuleRegistrationName.EVENT_BUS) const eventBus = container.resolve(ModuleRegistrationName.EVENT_BUS)
await eventBus.emit({ await eventBus.emit({
@@ -1547,7 +1543,6 @@ export const notifyRestaurantStep = createStep(
}) })
} }
) )
``` ```
In this step, you: In this step, you:
@@ -1600,7 +1595,7 @@ import { CreateOrderShippingMethodDTO } from "@medusajs/types"
import { import {
ModuleRegistrationName, ModuleRegistrationName,
Modules, Modules,
remoteQueryObjectFromString, ContainerRegistrationKeys,
} from "@medusajs/utils" } from "@medusajs/utils"
import { StepResponse, createStep } from "@medusajs/workflows-sdk" import { StepResponse, createStep } from "@medusajs/workflows-sdk"
import { DELIVERY_MODULE } from "../../../modules/delivery" import { DELIVERY_MODULE } from "../../../modules/delivery"
@@ -1608,9 +1603,9 @@ import { DELIVERY_MODULE } from "../../../modules/delivery"
export const createOrderStep = createStep( export const createOrderStep = createStep(
"create-order-step", "create-order-step",
async function (deliveryId: string, { container }) { 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", entryPoint: "deliveries",
variables: { variables: {
filters: { filters: {
@@ -1627,8 +1622,6 @@ export const createOrderStep = createStep(
], ],
}) })
const delivery = await remoteQuery(deliveryQuery).then((res) => res[0])
// TODO create order // TODO create order
}, },
async ({ orderId }, { container }) => { 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 = [ export const isDeliveryRestaurantHighlights = [
["21", "restaurantAdmin", "Retrieve the logged-in restaurant admin."], ["21", "restaurantAdmin", "Retrieve the logged-in restaurant admin."],
["28", "query", "Retrieve the delivery based on the ID in the path parameter."], ["28", "graph", "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."] ["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" ```ts title="src/api/utils/is-delivery-restaurant.ts" highlights={isDeliveryRestaurantHighlights} collapsibleLines="1-10" expandButtonLabel="Show Imports"
@@ -2420,7 +2413,7 @@ import {
MedusaResponse, MedusaResponse,
} from "@medusajs/medusa" } from "@medusajs/medusa"
import { import {
remoteQueryObjectFromString, ContainerRegistrationKeys,
} from "@medusajs/utils" } from "@medusajs/utils"
import { RESTAURANT_MODULE } from "../../modules/restaurant" import { RESTAURANT_MODULE } from "../../modules/restaurant"
@@ -2429,7 +2422,7 @@ export const isDeliveryRestaurant = async (
res: MedusaResponse, res: MedusaResponse,
next: MedusaNextFunction next: MedusaNextFunction
) => { ) => {
const remoteQuery = req.scope.resolve("remoteQuery") const query = req.scope.resolve(ContainerRegistrationKeys.QUERY)
const restaurantModuleService = req.scope.resolve( const restaurantModuleService = req.scope.resolve(
RESTAURANT_MODULE RESTAURANT_MODULE
) )
@@ -2441,7 +2434,7 @@ export const isDeliveryRestaurant = async (
} }
) )
const query = remoteQueryObjectFromString({ const { data: [delivery] } = await query.graph({
entryPoint: "delivery", entryPoint: "delivery",
fields: [ fields: [
"restaurant.*", "restaurant.*",
@@ -2453,9 +2446,7 @@ export const isDeliveryRestaurant = async (
}, },
}) })
const result = await remoteQuery(query) if (delivery.restaurant.id !== restaurantAdmin.restaurant.id) {
if (result[0].restaurant.id !== restaurantAdmin.restaurant.id) {
return res.status(403).json({ return res.status(403).json({
message: "unauthorized", message: "unauthorized",
}) })
@@ -563,13 +563,13 @@ To create the API route that retrieves the vendors products, create the file
export const retrieveProductHighlights = [ export const retrieveProductHighlights = [
["16", "retrieveVendorAdmin", "Retrive the vendor admin to retrieve its vendor's ID."], ["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} ```ts title="src/api/vendors/products/route.ts" highlights={retrieveProductHighlights}
import { AuthenticatedMedusaRequest, MedusaResponse } from "@medusajs/medusa" import { AuthenticatedMedusaRequest, MedusaResponse } from "@medusajs/medusa"
import { import {
remoteQueryObjectFromString, ContainerRegistrationKeys,
} from "@medusajs/utils" } from "@medusajs/utils"
import MarketplaceModuleService from "../../../modules/marketplace/service" import MarketplaceModuleService from "../../../modules/marketplace/service"
import { MARKETPLACE_MODULE } from "../../../modules/marketplace" import { MARKETPLACE_MODULE } from "../../../modules/marketplace"
@@ -578,7 +578,7 @@ export const GET = async (
req: AuthenticatedMedusaRequest, req: AuthenticatedMedusaRequest,
res: MedusaResponse res: MedusaResponse
) => { ) => {
const remoteQuery = req.scope.resolve("remoteQuery") const query = req.scope.resolve(ContainerRegistrationKeys.QUERY)
const marketplaceModuleService: MarketplaceModuleService = const marketplaceModuleService: MarketplaceModuleService =
req.scope.resolve(MARKETPLACE_MODULE) req.scope.resolve(MARKETPLACE_MODULE)
@@ -589,7 +589,7 @@ export const GET = async (
} }
) )
const query = remoteQueryObjectFromString({ const { data: [vendor] } = await query.graph({
entryPoint: "vendor", entryPoint: "vendor",
fields: ["products.*"], fields: ["products.*"],
variables: { variables: {
@@ -599,15 +599,13 @@ export const GET = async (
}, },
}) })
const result = await remoteQuery(query)
res.json({ 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: 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 ### 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) - [How to use the Remote Link](!docs!/advanced-development/modules/remote-link)
--- ---
@@ -817,7 +815,7 @@ import {
StepResponse, StepResponse,
} from "@medusajs/workflows-sdk" } from "@medusajs/workflows-sdk"
import { CartDTO, CartLineItemDTO } from "@medusajs/types" import { CartDTO, CartLineItemDTO } from "@medusajs/types"
import { remoteQueryObjectFromString } from "@medusajs/utils" import { ContainerRegistrationKeys } from "@medusajs/utils"
type StepInput = { type StepInput = {
cart: CartDTO cart: CartDTO
@@ -826,12 +824,12 @@ type StepInput = {
const groupVendorItemsStep = createStep( const groupVendorItemsStep = createStep(
"group-vendor-items", "group-vendor-items",
async ({ cart }: StepInput, { container }) => { async ({ cart }: StepInput, { container }) => {
const remoteQuery = container.resolve("remoteQuery") const query = container.resolve(ContainerRegistrationKeys.QUERY)
const vendorsItems: Record<string, CartLineItemDTO[]> = {} const vendorsItems: Record<string, CartLineItemDTO[]> = {}
await Promise.all(cart.items?.map(async (item) => { await Promise.all(cart.items?.map(async (item) => {
const query = remoteQueryObjectFromString({ const { data: [product] } = await query.graph({
entryPoint: "product", entryPoint: "product",
fields: ["vendor.*"], fields: ["vendor.*"],
variables: { variables: {
@@ -841,9 +839,7 @@ const groupVendorItemsStep = createStep(
}, },
}) })
const result = await remoteQuery(query) const vendorId = product.vendor?.id
const vendorId = result[0].vendor?.id
if (!vendorId) { if (!vendorId) {
return return
@@ -1251,13 +1247,13 @@ Create the file `src/api/vendors/orders/route.ts` with the following content:
export const getOrderHighlights = [ export const getOrderHighlights = [
["15", "retrieveVendorAdmin", "Retrive the vendor admin to retrieve its vendor's ID."], ["15", "retrieveVendorAdmin", "Retrive the vendor admin to retrieve its vendor's ID."],
["32", "remoteQuery", "Retrieve the orders of the vendor."], ["22", "graph", "Retrieve the orders of the vendor."],
["34", "getOrdersListWorkflow", "Use Medusa's workflow to retrieve the list of orders."], ["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" ```ts title="src/api/vendors/orders/route.ts" highlights={getOrderHighlights} collapsibleLines="1-6" expandMoreLabel="Show Imports"
import { AuthenticatedMedusaRequest, MedusaResponse } from "@medusajs/medusa" import { AuthenticatedMedusaRequest, MedusaResponse } from "@medusajs/medusa"
import { remoteQueryObjectFromString } from "@medusajs/utils" import { ContainerRegistrationKeys } from "@medusajs/utils"
import { getOrdersListWorkflow } from "@medusajs/core-flows" import { getOrdersListWorkflow } from "@medusajs/core-flows"
import MarketplaceModuleService from "../../../modules/marketplace/service" import MarketplaceModuleService from "../../../modules/marketplace/service"
import { MARKETPLACE_MODULE } from "../../../modules/marketplace" import { MARKETPLACE_MODULE } from "../../../modules/marketplace"
@@ -1266,7 +1262,7 @@ export const GET = async (
req: AuthenticatedMedusaRequest, req: AuthenticatedMedusaRequest,
res: MedusaResponse res: MedusaResponse
) => { ) => {
const remoteQuery = req.scope.resolve("remoteQuery") const query = req.scope.resolve(ContainerRegistrationKeys.QUERY)
const marketplaceModuleService: MarketplaceModuleService = const marketplaceModuleService: MarketplaceModuleService =
req.scope.resolve(MARKETPLACE_MODULE) req.scope.resolve(MARKETPLACE_MODULE)
@@ -1277,7 +1273,7 @@ export const GET = async (
} }
) )
const query = remoteQueryObjectFromString({ const { data: [vendor] } = await query.graph({
entryPoint: "vendor", entryPoint: "vendor",
fields: ["orders.*"], fields: ["orders.*"],
variables: { variables: {
@@ -1287,8 +1283,6 @@ export const GET = async (
}, },
}) })
const result = await remoteQuery(query)
const { result: orders } = await getOrdersListWorkflow(req.scope) const { result: orders } = await getOrdersListWorkflow(req.scope)
.run({ .run({
input: { input: {
@@ -1310,7 +1304,7 @@ export const GET = async (
], ],
variables: { variables: {
filters: { filters: {
id: result[0].orders.map((order) => order.id), id: vendor.orders.map((order) => order.id),
}, },
}, },
}, },
@@ -654,7 +654,7 @@ In this step, youll 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: Create the file `src/api/store/carts/[id]/complete/route.ts` with the following content:
export const completeCartHighlights = [ 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"], ["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."] ["38", "createSubscriptionWorkflow", "Execute the workflow created in the previous step."]
] ]
@@ -665,7 +665,7 @@ import {
MedusaResponse, MedusaResponse,
} from "@medusajs/medusa" } from "@medusajs/medusa"
import { import {
remoteQueryObjectFromString, ContainerRegistrationKeys,
MedusaError, MedusaError,
} from "@medusajs/utils" } from "@medusajs/utils"
import createSubscriptionWorkflow from "../../../../../workflows/create-subscription" import createSubscriptionWorkflow from "../../../../../workflows/create-subscription"
@@ -674,9 +674,9 @@ export const POST = async (
req: MedusaRequest, req: MedusaRequest,
res: MedusaResponse 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", entryPoint: "cart",
fields: [ fields: [
"metadata", "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) { if (!metadata?.subscription_interval || !metadata.subscription_period) {
throw new MedusaError( throw new MedusaError(
@@ -973,7 +973,7 @@ In this step, youll add two API routes for admin users:
Create the file `src/api/admin/subscriptions/route.ts` with the following content: Create the file `src/api/admin/subscriptions/route.ts` with the following content:
export const listSubscriptionsAdminHighlight = [ 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} ```ts title="src/api/admin/subscriptions/route.ts" highlights={listSubscriptionsAdminHighlight}
@@ -981,20 +981,23 @@ import {
AuthenticatedMedusaRequest, AuthenticatedMedusaRequest,
MedusaResponse, MedusaResponse,
} from "@medusajs/medusa" } from "@medusajs/medusa"
import { remoteQueryObjectFromString } from "@medusajs/utils" import { ContainerRegistrationKeys } from "@medusajs/utils"
export const GET = async ( export const GET = async (
req: AuthenticatedMedusaRequest, req: AuthenticatedMedusaRequest,
res: MedusaResponse res: MedusaResponse
) => { ) => {
const remoteQuery = req.scope.resolve("remoteQuery") const query = req.scope.resolve(ContainerRegistrationKeys.QUERY)
const { const {
limit = 20, limit = 20,
offset = 0, offset = 0,
} = req.validatedQuery || {} } = req.validatedQuery || {}
const query = remoteQueryObjectFromString({ const {
data: subscriptions,
metadata: { count, take, skip },
} = await query.graph({
entryPoint: "subscription", entryPoint: "subscription",
fields: [ fields: [
"*", "*",
@@ -1011,13 +1014,8 @@ export const GET = async (
}, },
}) })
const {
rows,
metadata: { count, take, skip },
} = await remoteQuery(query)
res.json({ res.json({
subscriptions: rows, subscriptions,
count, count,
limit: take, limit: take,
offset: skip, offset: skip,
@@ -1027,7 +1025,7 @@ export const GET = async (
This adds a `GET` API route at `/admin/subscriptions`. 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. 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: Create the file `src/api/admin/subscriptions/[id]/route.ts` with the following content:
export const getSubscriptionsAdminHighlight = [ 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} ```ts title="src/api/admin/subscriptions/[id]/route.ts" highlights={getSubscriptionsAdminHighlight}
@@ -1044,15 +1042,15 @@ import {
AuthenticatedMedusaRequest, AuthenticatedMedusaRequest,
MedusaResponse, MedusaResponse,
} from "@medusajs/medusa" } from "@medusajs/medusa"
import { remoteQueryObjectFromString } from "@medusajs/utils" import { ContainerRegistrationKeys } from "@medusajs/utils"
export const GET = async ( export const GET = async (
req: AuthenticatedMedusaRequest, req: AuthenticatedMedusaRequest,
res: MedusaResponse 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", entryPoint: "subscription",
fields: [ fields: [
"*", "*",
@@ -1067,17 +1065,15 @@ export const GET = async (
}, },
}) })
const result = await remoteQuery(query)
res.json({ 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. 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, youll extend the Medusa admin and use these API routes to show the subscriptions. In the next section, youll extend the Medusa admin and use these API routes to show the subscriptions.
@@ -2130,15 +2126,15 @@ import {
AuthenticatedMedusaRequest, AuthenticatedMedusaRequest,
MedusaResponse, MedusaResponse,
} from "@medusajs/medusa" } from "@medusajs/medusa"
import { remoteQueryObjectFromString } from "@medusajs/utils" import { ContainerRegistrationKeys } from "@medusajs/utils"
export const GET = async ( export const GET = async (
req: AuthenticatedMedusaRequest, req: AuthenticatedMedusaRequest,
res: MedusaResponse 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", entryPoint: "customer",
fields: [ fields: [
"subscriptions.*", "subscriptions.*",
@@ -2150,17 +2146,15 @@ export const GET = async (
}, },
}) })
const result = await remoteQuery(query)
res.json({ res.json({
subscriptions: result[0].subscriptions, subscriptions: customer.subscriptions,
}) })
} }
``` ```
This adds an API route at `/store/customers/me/subscriptions`. This adds an API route at `/store/customers/me/subscriptions`.
In the route handler, you retrieve the authenticated customers subscriptions using the remote query and return them in the response. In the route handler, you retrieve the authenticated customers subscriptions using Query and return them in the response.
### Cancel Subscription API Route ### Cancel Subscription API Route
@@ -48,7 +48,7 @@ The method returns an array of the first `15` records matching the filters.
<Note> <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> </Note>
@@ -54,7 +54,7 @@ The method returns an array with two items:
<Note> <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> </Note>
@@ -30,7 +30,7 @@ The method returns the record as an object.
<Note> <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> </Note>