docs: add routing page (#9550)

- Add a new homepage to `book` project for the routing page
- Move all main doc pages to be under `/v2/learn` (and added redirects + fixed links across docs)
- Other: add admin components to resources dropdown + fixes to search on mobile.

Closes DX-955

Preview: https://docs-v2-git-docs-router-page-medusajs.vercel.app/v2
This commit is contained in:
Shahed Nasser
2024-10-18 08:24:34 +00:00
committed by GitHub
parent 7a47f5211d
commit 0a37675f0e
223 changed files with 2549 additions and 696 deletions
@@ -0,0 +1,174 @@
export const metadata = {
title: `${pageNumber} Add Columns to a Link`,
}
# {metadata.title}
In this chapter, you'll learn how to add custom columns to a link definition and manage them.
## How to Add Custom Columns to a Link's Table?
The `defineLink` function used to define a link accepts a third paramter, which is an object of options.
To add custom columns to a link's table, pass in the third parameter of `defineLink` a `database` property:
export const linkHighlights = [
["10", "extraColumns", "Custom columns to add to the created link's table."],
["11", "metadata", "The column's name."],
["12", "type", "The column's type."]
]
```ts highlights={linkHighlights}
import HelloModule from "../modules/hello"
import ProductModule from "@medusajs/medusa/product"
import { defineLink } from "@medusajs/framework/utils"
export default defineLink(
ProductModule.linkable.product,
HelloModule.linkable.myCustom,
{
database: {
extraColumns: {
metadata: {
type: "json",
},
},
},
}
)
```
This adds to the table created for the link between `product` and `myCustom` a `metadata` column of type `json`.
### Database Options
The `database` property defines configuration for the table created in the database.
Its `extraColumns` property defines custom columns to create in the link's table.
`extraColumns`'s value is an object whose keys are the names of the columns, and values are the column's configurations as an object.
### Column Configurations
The column's configurations object accepts the following properties:
- `type`: The column's type. Possible values are:
- `string`
- `text`
- `integer`
- `boolean`
- `date`
- `time`
- `datetime`
- `enum`
- `json`
- `array`
- `enumArray`
- `float`
- `double`
- `decimal`
- `bigint`
- `mediumint`
- `smallint`
- `tinyint`
- `blob`
- `uuid`
- `uint8array`
- `defaultValue`: The column's default value.
- `nullable`: Whether the column can have `null` values.
---
## Set Custom Column when Creating Link
The object you pass to the remote link's `create` method accepts a `data` property. Its value is an object whose keys are custom column names, and values are the value of the custom column for this link.
For example:
<Note>
Learn more about the remote link, how to resolve it, and its methods in [this chapter](../remote-link/page.mdx).
</Note>
```ts
await remoteLink.create({
[Modules.PRODUCT]: {
product_id: "123",
},
HELLO_MODULE: {
my_custom_id: "321",
},
data: {
metadata: {
test: true,
},
},
})
```
---
## Retrieve Custom Column with Link
To retrieve linked records with their custom columns, use Query and pass the link definition as the `entity` property's value.
For example:
<Note>
Learn more about Query and how to resolve use it [this chapter](../remote-link/page.mdx).
</Note>
export const retrieveHighlights = [
["1", "productHelloLink", "Import the exported link definition."],
["6", "entity", "Pass the link definition to retrieve its data."],
["7", `"metadata"`, "Retrieve the `metadata` column."],
["7", `"product.*"`, "Retrieve the linked product's details."],
["7", `"my_custom.*"`, "Retrieve the linked `myCustom` record's details."],
]
```ts highlights={retrieveHighlights}
import productHelloLink from "../links/product-hello"
// ...
const { data } = await query.graph({
entity: productHelloLink.entryPoint,
fields: ["metadata", "product.*", "my_custom.*"],
filters: {
product_id: "prod_123",
},
})
```
This retrieves the product of id `prod_123` and its linked `my_custom` records.
In the `fields` array you pass `metadata`, which is the custom column to retrieve of the link.
---
## Update Custom Column's Value
The remote link's `create` method updates a link's data if the link between the specified records already exists.
So, to update the value of a custom column in a created link, use the `create` method again passing it a new value for the custom column.
For example:
```ts
await remoteLink.create({
[Modules.PRODUCT]: {
product_id: "123",
},
HELLO_MODULE: {
my_custom_id: "321",
},
data: {
metadata: {
test: false,
},
},
})
```
@@ -0,0 +1,61 @@
export const metadata = {
title: `${pageNumber} Module Link Direction`,
}
# {metadata.title}
In this chapter, you'll learn about the difference in module link directions, and which to use based on your use case.
## Link Direction
The module link's direction depends on the order you pass the data model configuration parameters to `defineLink`.
For example, the following defines a link from the `helloModuleService`'s `myCustom` data model to the Product Module's `product` data model:
```ts
export default defineLink(
HelloModule.linkable.myCustom,
ProductModule.linkable.product
)
```
Whereas the following defines a link from the Product Module's `product` data model to the `helloModuleService`'s `myCustom` data model:
```ts
export default defineLink(
ProductModule.linkable.product,
HelloModule.linkable.myCustom
)
```
The above links are two different links that serve different purposes.
---
## Which Link Direction to Use?
### Extend Data Models
If you're adding a link to a data model to extend it and add new fields, define the link from the main data model to the custom data model.
For example, if the `myCustom` data model adds new fields to the `product` data model, define the link from `product` to `myCustom`:
```ts
export default defineLink(
ProductModule.linkable.product,
HelloModule.linkable.myCustom
)
```
### Associate Data Models
If you're linking data models to indicate an association between them, define the link from the custom data model to the main data model.
For example, if the `myCustom` data model is associated to the `product` data model, define the link from `myCustom` to `product`:
```ts
export default defineLink(
HelloModule.linkable.myCustom,
ProductModule.linkable.product
)
```
@@ -0,0 +1,143 @@
export const metadata = {
title: `${pageNumber} Module Link`,
}
# {metadata.title}
In this chapter, youll learn what a module link is.
## What is a Module Link?
Since modules are isolated, you can't access another module's data models to add a relation to it or extend it.
Instead, you use a module link. A module link forms an association between two data models of different modules, while maintaining module isolation.
---
## How to Define a Module Link?
### 1. Create Link File
Links are defined in a TypeScript or JavaScript file under the `src/links` directory. The file defines the link using the `defineLink` function imported from `@medusajs/framework/utils` and exports it.
For example:
export const highlights = [
["6", "linkable", "Special `linkable` property that holds the linkable data models of `HelloModule`."],
["7", "linkable", "Special `linkable` property that holds the linkable data models of `ProductModule`."],
]
```ts title="src/links/hello-product.ts" highlights={highlights}
import HelloModule from "../modules/hello"
import ProductModule from "@medusajs/medusa/product"
import { defineLink } from "@medusajs/framework/utils"
export default defineLink(
ProductModule.linkable.product,
HelloModule.linkable.myCustom
)
```
The `defineLink` function accepts as parameters the link configurations of each module's data model. A module has a special `linkable` property that holds these configurations for its data models.
In this example, you define a module link between the `hello` module's `MyCustom` data model and the Product Module's `Product` data model.
### 2. Sync Links
After defining the link, run the `db:sync-links` command:
```bash
npx medusa db:sync-links
```
The Medusa application creates a new table for your link to store the IDs of linked records.
Use this command whenever you make changes to your links. For example, run this command if you remove your link definition file.
<Note title="Tip">
You can also use the `db:migrate` command, which both runs the migrations and syncs the links.
</Note>
---
## How Module Links Work?
When you define a module link, the Medusa application creates a table in the database for that link.
Then, when you create links between records of the data models, the IDs of these data models are stored as a new record in the link's table.
![Diagram illustration for links](https://res.cloudinary.com/dza7lstvk/image/upload/v1726482168/Medusa%20Book/Custom_Link_Illustration_fsisfa.jpg)
---
## When to Use Module Links
<Note title="Use module links when" type="success">
- You want to create a relation between data models from different modules.
- You want to extend the data model of another module.
</Note>
<Note title="Don't use module links if" type="error">
You want to create a relationship between data models in the same module. Use data model relationships instead.
</Note>
---
## Define a List Link
By default, the defined link establishes a one-to-one relation: a record of a data model is linked to one record of the other data model.
To specify that a data model can have multiple of its records linked to the other data model's record, use the `isList` option.
For example:
```ts
import HelloModule from "../modules/hello"
import ProductModule from "@medusajs/medusa/product"
import { defineLink } from "@medusajs/framework/utils"
export default defineLink(
ProductModule.linkable.product,
{
linkable: HelloModule.linkable.myCustom,
isList: true,
}
)
```
In this case, you pass an object of configuration as a parameter instead. The object accepts the following properties:
- `linkable`: The data model's link configuration.
- `isList`: Whether multiple records can be linked to one record of the other data model.
In this example, a record of `product` can be linked to more than one record of `myCustom`.
---
## Set Delete Cascades on Link
To enable delete cascade on a link so that when a record is deleted, its linked records are also deleted, pass the `deleteCascades` property in the object passed to `defineLink`.
For example:
```ts
import HelloModule from "../modules/hello"
import ProductModule from "@medusajs/medusa/product"
import { defineLink } from "@medusajs/framework/utils"
export default defineLink(
ProductModule.linkable.product,
{
linkable: HelloModule.linkable.myCustom,
deleteCascades: true,
}
)
```
In this example, when a product is deleted, its linked `myCustom` record is also deleted.
@@ -0,0 +1,229 @@
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 type="soon" title="In Development">
Query is in development and is subject to change in future releases.
</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/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", "entity", "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/query/route.ts" highlights={exampleHighlights} collapsibleLines="1-8" expandButtonLabel="Show 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: myCustoms } = await query.graph({
entity: "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:
- `entity`: 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"
}
]
}
```
---
## Querying the Graph
When you use the `query.graph` method, you're running a query through an internal graph that the Medusa application creates.
This graph collects data models of all modules in your application, including commerce and custom modules, and identifies relations and links between them.
---
## 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({
entity: "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({
entity: "my_custom",
fields: [
"id",
"name",
"products.*",
],
})
```
---
## Apply Filters
```ts highlights={[["6"], ["7"], ["8"], ["9"]]}
const { data: myCustoms } = await query.graph({
entity: "my_custom",
fields: ["id", "name"],
filters: {
id: [
"mc_01HWSVWR4D2XVPQ06DQ8X9K7AX",
"mc_01HWSVWK3KYHKQEE6QGS2JC3FX",
],
},
})
```
The `query.graph` function accepts a `filters` property. You can use this property to filter retrieved records.
In the example above, you filter the `my_custom` records by multiple IDs.
<Note>
Filters don't apply on fields of linked data models from other modules.
</Note>
---
## Sort Records
```ts highlights={[["5"], ["6"], ["7"]]}
const { data: myCustoms } = await query.graph({
entity: "my_custom",
fields: ["id", "name"],
pagination: {
order: {
name: "DESC",
},
},
})
```
<Note>
Sorting doesn't work on fields of linked data models from other modules.
</Note>
The `graph` method's object parameter accepts a `pagination` property to configure the pagination of retrieved records.
To sort returned records, pass an `order` property to `pagination`.
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({
entity: "my_custom",
fields: ["id", "name"],
pagination: {
skip: 0,
take: 10,
},
})
```
To paginate the returned records, pass the following properties to `pagination`:
- `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" />
@@ -0,0 +1,153 @@
import { BetaBadge } from "docs-ui"
export const metadata = {
title: `${pageNumber} Remote Link`,
}
# {metadata.title} <BetaBadge text="Beta" tooltipText="Remote Links are in active development." />
In this chapter, youll learn what the remote link is and how to use it to manage links.
## What is the Remote Link?
The remote link is a class with utility methods to manage links between data models. Its registered in the Medusa container under the `remoteLink` registration name.
For example:
```ts collapsibleLines="1-9" expandButtonLabel="Show Imports"
import {
MedusaRequest,
MedusaResponse,
} from "@medusajs/framework/http"
import {
ContainerRegistrationKeys,
} from "@medusajs/framework/utils"
import {
RemoteLink,
} from "@medusajs/framework/modules-sdk"
export async function POST(
req: MedusaRequest,
res: MedusaResponse
): Promise<void> {
const remoteLink: RemoteLink = req.scope.resolve(
ContainerRegistrationKeys.REMOTE_LINK
)
// ...
}
```
You can use its methods to manage links, such as create or delete links.
---
## Create Link
To create a link between records of two data models, use the `create` method of the remote link.
For example:
```ts
import { Modules } from "@medusajs/framework/utils"
// ...
await remoteLink.create({
[Modules.PRODUCT]: {
product_id: "prod_123",
},
"helloModuleService": {
my_custom_id: "mc_123",
},
})
```
The `create` method accepts as a parameter an object. The objects keys are the names of the linked modules.
<Note title="Important">
The keys (names of linked modules) must be in the same direction of the link definition.
</Note>
The value of each modules property is an object, whose keys are of the format `{data_model_snake_name}_id`, and values are the IDs of the linked record.
So, in the example above, you link a record of the `MyCustom` data model in a `hello` module to a `Product` record in the Product Module.
---
## Dismiss Link
To remove a link between records of two data models, use the `dismiss` method of the remote link.
For example:
```ts
import { Modules } from "@medusajs/framework/utils"
// ...
await remoteLink.dismiss({
[Modules.PRODUCT]: {
product_id: "prod_123",
},
"helloModuleService": {
my_custom_id: "mc_123",
},
})
```
The `dismiss` method accepts the same parameter type as the [create method](#create-link).
<Note title="Important">
The keys (names of linked modules) must be in the same direction of the link definition.
</Note>
---
## Cascade Delete Linked Records
If a record is deleted, use the `delete` method of the remote link to delete all linked records.
For example:
```ts
import { Modules } from "@medusajs/framework/utils"
// ...
await productModuleService.deleteVariants([variant.id])
await remoteLink.delete({
[Modules.PRODUCT]: {
product_id: "prod_123",
},
})
```
This deletes all records linked to the deleted product.
---
## Restore Linked Records
If a record that was previously soft-deleted is now restored, use the `restore` method of the remote link to restore all linked records.
For example:
```ts
import { Modules } from "@medusajs/framework/utils"
// ...
await productModuleService.restoreProducts(["prod_123"])
await remoteLink.restore({
[Modules.PRODUCT]: {
product_id: "prod_123",
},
})
```