Skip to content

Commit

Permalink
docs: revise query linked records (#10447)
Browse files Browse the repository at this point in the history
  • Loading branch information
shahednasser authored Dec 5, 2024
1 parent 9690e44 commit 7013c37
Show file tree
Hide file tree
Showing 3 changed files with 105 additions and 126 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,6 @@ This chapter covers how to show the brand of a product in the Medusa Admin using

## Widget to Show Brand in Product Details

<Prerequisites
items={[
{
text: "Retrieve Brand of Product API Route",
link: "/learn/customization/extend-features/query-linked-records"
}
]}
/>

To create a widget that shows a product's brand in its details page, create the file `src/admin/widgets/product-brand.tsx` with the following content:

export const highlights = [
Expand Down Expand Up @@ -51,12 +42,12 @@ const ProductBrandWidget = ({
return
}

fetch(`/admin/products/${data.id}/brand`, {
fetch(`/admin/products/${data.id}?fields=+brand.*`, {
credentials: "include",
})
.then((res) => res.json())
.then(({ brand }) => {
setBrand(brand)
.then(({ product }) => {
setBrand(product.brand)
setLoading(false)
})
}, [loading])
Expand Down Expand Up @@ -89,7 +80,7 @@ Learn more about widgets [in this guide](../../../basics/admin-customizations/pa

Widgets created in a details page receive the targetted item in a `data` prop. So, the `ProductBrandWidget` receives the product's details in the `data` prop.

In the widget, you fetch the product's brand from the `/admin/products/:id/brand` API route and display it.
In the widget, you fetch the product's brand using the [Get Product API route](!api!/admin#products_getproductsid), passing it the query parameter `fields=+brand.*` to retrieve the product's brand.

<Note>

Expand Down
Original file line number Diff line number Diff line change
@@ -1,24 +1,14 @@
import { Prerequisites } from "docs-ui"

export const metadata = {
title: `${pageNumber} Retrieve Brand linked to Product using Query`,
title: `${pageNumber} Guide: Query Product's Brands`,
}

# {metadata.title}

<Note title="Example Chapter">
In the previous chapters, you [defined a link](../define-link/page.mdx) between the [custom Brand Module](../../custom-features/module/page.mdx) and Medusa's [Product Module](!resources!/comerce-modules/product), then [extended the create-product flow](../extend-create-product/page.mdx) to link a product to a brand.

This chapter covers how to retrieve the brand linked to a product using Query as a step of the ["Extend Models" chapter](../page.mdx).

</Note>

## What is Query?

Query is a utility that retrieves data across modules and their links. It’s registered in the Medusa container under the `ContainerRegistrationKeys.QUERY` (`query`) registration name.

---

## Retrieve Brand of Product API Route
In this chapter, you'll learn how to retrieve a product's brand (and vice-versa) in two ways: Using Medusa's existing API route, or in customizations, such as a custom API route.

<Prerequisites
items={[
Expand All @@ -33,136 +23,132 @@ Query is a utility that retrieves data across modules and their links. It’s re
]}
/>

You'll create an API route that retrieves the brand of a product. You'll use this in a later chapter.
---

## Approach 1: Retrieve Brands in Existing API Routes

Medusa's existing API routes accept a `fields` query parameter that allows you to specify the fields and relations of a model to retrieve. So, when you send a request to the [List Products](!api!/admin#products_getproducts), [Get Product](!api!/admin#products_getproductsid), or any product-related store or admin routes that accept a `fields` query parameter, you can specify in this parameter to return the product's brands.

<Note>

Learn more about selecting fields and relations in the [API Reference](!api!/admin#select-fields-and-relations).

</Note>

For example, send the following request to retrieve the list of products with their brands:

```bash
curl 'http://localhost:9000/admin/products?fields=+brand.*' \
--header 'Authorization: Bearer {token}'
```

<Note title="Tip">

Create the file `src/api/admin/products/[id]/brand/route.ts` with the following content:
Make sure to replace `{token}` with your admin user's authentication token. Learn how to retrieve it in the [API reference](!api!/store#authentication).

</Note>

Any product that is linked to a brand will have a `brand` property in its object:

```json title="Example Product Object"
{
"id": "prod_123",
// ...
"brand": {
"id": "01JEB44M61BRM3ARM2RRMK7GJF",
"name": "Acme",
"created_at": "2024-12-05T09:59:08.737Z",
"updated_at": "2024-12-05T09:59:08.737Z",
"deleted_at": null
}
}
```

By using the `fields` query parameter, you don't have to re-create existing API routes to get custom data models that you linked to core data models.

---

## Approach 2: Use Query to Retrieve Linked Records

You can also retrieve linked records using Query. Query allows you to retrieve data across modules with filters, pagination, and more. You can resolve Query from the Medusa container and use it in your API route or workflow.

<Note>

Learn more about Query in [this chapter](../../../advanced-development/module-links/query/page.mdx).

</Note>

For example, you can create an API route that retrieves brands and their products. If you followed the [Create Brands API route chapter](../../custom-features/api-route/page.mdx), you'll have the file `src/api/admin/brands/route.ts` with a `POST` API route. Add a new `GET` function to the same file:

export const highlights = [
["13", "resolve", "Resolve Query from the Medusa Container."],
["17", "graph", "Run a query to retrieve a product by its ID and its brand."],
["18", "entity", "The name of the model to query."],
["19", "fields", "The fields and relations to retrieve."],
["20", "filters", "The filters to apply on the retrieved data."]
["11", "resolve", "Resolve Query from the Medusa container."],
["13", "graph", "Run a query to retrieve brands with their linked products."],
["14", "entity", "The name of the model to query."],
["15", "fields", "The fields and relations to retrieve."]
]

```ts title="src/api/admin/products/[id]/brand/route.ts" highlights={highlights} collapsibleLines="1-8" expandButtonLabel="Show Imports"
```ts title="src/api/admin/brands/route.ts" highlights={highlights}
// other imports...
import {
MedusaRequest,
MedusaResponse,
} from "@medusajs/framework/http"
import {
ContainerRegistrationKeys,
} from "@medusajs/framework/utils"

export const GET = async (
req: MedusaRequest,
res: MedusaResponse
) => {
const query = req.scope.resolve(
ContainerRegistrationKeys.QUERY
)

const { data: [product] } = await query.graph({
entity: "product",
fields: ["brand.*"],
filters: {
id: req.params.id,
},
const query = req.scope.resolve("query")

const { data: brands } = await query.graph({
entity: "brand",
fields: ["*", "products.*"],
})

res.json({ brand: product.brand })
res.json({ brands })
}
```

In this example, you retrieve a product by its ID with its brand, and return the brand in the response.

### query.graph Parameters

The `graph` method of Query runs a query to retrieve data. It accepts an object having the following properties:
This adds a `GET` API route at `/admin/brands`. In the API route, you resolve Query from the Medusa container. Query has a `graph` method that runs a query to retrieve data. It accepts an object having the following properties:

- `entity`: The data model's name as specified in the first parameter of `model.define`.
- `fields`: An array of properties and relations to retrieve. You can pass:
- A property's name, such as `id`.
- A relation or linked model's name, such as `brand`. You suffix the name with `.*` to retrieve all its properties.
- `filters`: An object of filters to apply on the retrieved data model's properties.
- A property's name, such as `id`, or `*` for all properties.
- A relation or linked model's name, such as `products` (use the plural name since brands are linked to list of products). You suffix the name with `.*` to retrieve all its properties.

<Note>
`graph` returns an object having a `data` property, which is the retrieved brands. You return the brands in the response.

Filters currently don't work on models of another module, such as `brand` in this example.
### Test it Out

</Note>

---

## Test it Out

To test the API route out, first, retrieve the authentication token of your admin user by sending a `POST` request to `/auth/user/emailpass`:
To test the API route out, send a `GET` request to `/admin/brands`:

```bash
curl -X POST 'http://localhost:9000/auth/user/emailpass' \
-H 'Content-Type: application/json' \
--data-raw '{
"email": "admin@medusa-test.com",
"password": "supersecret"
}'
```

Make sure to replace the email and password with your user's credentials.

Then, send a `GET` request to `/admin/products/:id/brand`:

```bash
curl 'http://localhost:9000/admin/products/prod_123/brand' \
curl 'http://localhost:9000/admin/brands' \
-H 'Authorization: Bearer {token}'
```

This returns the product's brand if it has one. For example:

```json title="Example Response"
{
"brand": {
"id": "123",
"name": "Acme",
// ...
}
}
```

---
<Note title="Tip">

## Retrieve Products of a Brand
Make sure to replace `{token}` with your admin user's authentication token. Learn how to retrieve it in the [API reference](!api!/store#authentication).

An example of retrieving the products of a brand:

export const brandProductsHighlights = [
["7", `"products.*"`, "Use the plural name of `product` since a brand has multiple products."]
]

```ts highlights={brandProductsHighlights}
const query = req.scope.resolve(
ContainerRegistrationKeys.QUERY
)

const { data: [brand] } = await query.graph({
entity: "brand",
fields: ["products.*"],
filters: {
id: req.params.id,
},
})
```

In this case, since a brand has multiple products, you specify the plural name of the `Product` data model (`products`) in `fields`.
</Note>

The retrieved `brand` now has a `products` field, which is an array of products linked to it:
This returns the brands in your store with their linked products. For example:

```json title="Example Response"
{
"brand": {
"products": [
"brands": [
{
"id": "123",
// ...
]
}
"products": [
{
"id": "prod_123",
// ...
}
]
}
]
}
```

Expand All @@ -172,12 +158,14 @@ The retrieved `brand` now has a `products` field, which is an array of products

By following the examples of the previous chapters, you:

- Defined a link between the Brand and Product modules's data models, as if you're extending the `Product` model to add a brand.
- Created a link between brand and product records.
- Queried the brand linked to a product, and vice versa.
- Defined a link between the Brand and Product modules's data models, allowing you to associate a product with a brand.
- Extended the create-product workflow and route to allow setting the product's brand while creating the product.
- Queried a product's brand, and vice versa.

---

## Next Steps
## Next Steps: Customize Medusa Admin

Clients, such as the Medusa Admin dashboard, can now use brand-related features, such as creating a brand or setting the brand of a product.

In the next chapters, you'll learn how to customize the Medusa Admin to show brands.
In the next chapters, you'll learn how to customize the Medusa Admin to show a product's brand on its details page, and to show a new page with the list of brands in your store.
4 changes: 2 additions & 2 deletions www/apps/book/generated/edit-dates.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -96,10 +96,10 @@ export const generatedEditDates = {
"app/learn/customization/custom-features/page.mdx": "2024-11-28T08:21:55.207Z",
"app/learn/customization/customize-admin/page.mdx": "2024-09-12T12:25:29.853Z",
"app/learn/customization/customize-admin/route/page.mdx": "2024-10-07T12:43:11.335Z",
"app/learn/customization/customize-admin/widget/page.mdx": "2024-10-07T12:44:24.538Z",
"app/learn/customization/customize-admin/widget/page.mdx": "2024-12-05T10:40:56.611Z",
"app/learn/customization/extend-features/define-link/page.mdx": "2024-12-04T17:15:16.004Z",
"app/learn/customization/extend-features/page.mdx": "2024-09-12T12:38:57.394Z",
"app/learn/customization/extend-features/query-linked-records/page.mdx": "2024-09-30T08:43:53.134Z",
"app/learn/customization/extend-features/query-linked-records/page.mdx": "2024-12-05T10:36:32.357Z",
"app/learn/customization/integrate-systems/handle-event/page.mdx": "2024-09-30T08:43:53.135Z",
"app/learn/customization/integrate-systems/page.mdx": "2024-09-12T12:33:29.827Z",
"app/learn/customization/integrate-systems/schedule-task/page.mdx": "2024-09-30T08:43:53.135Z",
Expand Down

0 comments on commit 7013c37

Please sign in to comment.