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:
@@ -0,0 +1,114 @@
|
||||
import { Prerequisites } from "docs-ui"
|
||||
|
||||
export const metadata = {
|
||||
title: `${pageNumber} Create Brand API Route`,
|
||||
}
|
||||
|
||||
# {metadata.title}
|
||||
|
||||
<Note title="Example Chapter">
|
||||
|
||||
This chapter covers how to define an API route that creates a brand as the last step of the ["Build Custom Features" chapter](../page.mdx).
|
||||
|
||||
</Note>
|
||||
|
||||
<Prerequisites
|
||||
items={[
|
||||
{
|
||||
text: "createBrandWorkflow",
|
||||
link: "/customization/custom-features/workflow"
|
||||
}
|
||||
]}
|
||||
/>
|
||||
|
||||
Create the file `src/api/admin/brands/route.ts` with the following content:
|
||||
|
||||
```ts title="src/api/admin/brands/route.ts" collapsibleLines="1-9" expandButtonLabel="Show Imports"
|
||||
import {
|
||||
MedusaRequest,
|
||||
MedusaResponse,
|
||||
} from "@medusajs/framework/http"
|
||||
import {
|
||||
CreateBrandInput,
|
||||
createBrandWorkflow,
|
||||
} from "../../../workflows/create-brand"
|
||||
|
||||
export const POST = async (
|
||||
req: MedusaRequest<CreateBrandInput>,
|
||||
res: MedusaResponse
|
||||
) => {
|
||||
const { result } = await createBrandWorkflow(req.scope)
|
||||
.run({
|
||||
input: req.body,
|
||||
})
|
||||
|
||||
res.json({ brand: result })
|
||||
}
|
||||
```
|
||||
|
||||
This adds a `POST` API route at `/admin/brands`. In the API route's handler, you execute the `createBrandWorkflow`, passing it the request body as input.
|
||||
|
||||
You return in the response the created brand.
|
||||
|
||||
<Note>
|
||||
|
||||
Learn more about API routes [in this guide](../../../basics/api-routes/page.mdx).
|
||||
|
||||
</Note>
|
||||
|
||||
---
|
||||
|
||||
## Test API Route
|
||||
|
||||
To test it out, first, retrieve an authenticated token of your admin user by sending a `POST` request to the `/auth/user/emailpass` API Route:
|
||||
|
||||
```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 `POST` request to `/admin/brands`, passing the token received from the previous request in the `Authorization` header:
|
||||
|
||||
```bash
|
||||
curl -X POST 'http://localhost:9000/admin/brands' \
|
||||
-H 'Content-Type: application/json' \
|
||||
-H 'Authorization: Bearer {token}' \
|
||||
--data '{
|
||||
"name": "Acme"
|
||||
}'
|
||||
```
|
||||
|
||||
This returns the created brand in the response:
|
||||
|
||||
```json title="Example Response"
|
||||
{
|
||||
"brand": {
|
||||
"id": "01J7AX9ES4X113HKY6C681KDZJ",
|
||||
"name": "Acme",
|
||||
"created_at": "2024-09-09T08:09:34.244Z",
|
||||
"updated_at": "2024-09-09T08:09:34.244Z"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Summary
|
||||
|
||||
By following the previous example chapters, you implemented a custom feature that allows admin users to create a brand by:
|
||||
|
||||
1. Creating a module that defines and manages the `Brand` data model.
|
||||
2. Creating a workflow that uses the module's main service to create a brand record, and implements the compensation logic to delete that brand in case an error occurs.
|
||||
3. Creating an API route that allows admin users to create a brand.
|
||||
|
||||
---
|
||||
|
||||
## Next Steps
|
||||
|
||||
In the next chapters, you'll learn how to extend data models and associate the brand with a product.
|
||||
@@ -0,0 +1,128 @@
|
||||
export const metadata = {
|
||||
title: `${pageNumber} Implement Brand Module`,
|
||||
}
|
||||
|
||||
# {metadata.title}
|
||||
|
||||
<Note title="Example Chapter">
|
||||
|
||||
This chapter covers how to create a Brand Module as part of the ["Build Custom Features" chapter](../page.mdx).
|
||||
|
||||
</Note>
|
||||
|
||||
## 1. Create Module Directory
|
||||
|
||||
Start by creating the directory `src/modules/brand` that will hold the Brand Module's files.
|
||||
|
||||
---
|
||||
|
||||
## 2. Create Data Model
|
||||
|
||||
To create a data model that represents a new `brand` table in the database, create the file `src/modules/brand/models/brand.ts` with the following content:
|
||||
|
||||
```ts title="src/modules/brand/models/brand.ts"
|
||||
import { model } from "@medusajs/framework/utils"
|
||||
|
||||
export const Brand = model.define("brand", {
|
||||
id: model.id().primaryKey(),
|
||||
name: model.text(),
|
||||
})
|
||||
```
|
||||
|
||||
This creates a `Brand` data model which has an `id` primary key property, and a `name` text property.
|
||||
|
||||
---
|
||||
|
||||
## 3. Create Module Service
|
||||
|
||||
Next, you'll create the module's main service that manages the `Brand` data model.
|
||||
|
||||
Create the file `src/modules/brand/service.ts` with the following content:
|
||||
|
||||
export const serviceHighlights = [
|
||||
["4", "MedusaService", "A service factory that generates data-management methods."]
|
||||
]
|
||||
|
||||
```ts title="src/modules/brand/service.ts" highlights={serviceHighlights}
|
||||
import { MedusaService } from "@medusajs/framework/utils"
|
||||
import { Brand } from "./models/brand"
|
||||
|
||||
class BrandModuleService extends MedusaService({
|
||||
Brand,
|
||||
}) {
|
||||
|
||||
}
|
||||
|
||||
export default BrandModuleService
|
||||
```
|
||||
|
||||
The `BrandModuleService` extends a `MedusaService` function imported from `@medusajs/framework/utils` which is a service factory.
|
||||
|
||||
The `MedusaService` function receives an object of the module's data models as a parameter, and generates methods to manage those data models, such as `createBrands` and `updateBrands`.
|
||||
|
||||
Those methods are now available at the `BrandModuleService` class and you'll use them in upcoming steps.
|
||||
|
||||
<Note title="Tip">
|
||||
|
||||
Find a reference of the generated methods in [this guide](!resources!/service-factory-reference).
|
||||
|
||||
</Note>
|
||||
|
||||
---
|
||||
|
||||
## 4. Create Module's Definition
|
||||
|
||||
To export the module's definition, create the file `src/modules/brand/index.ts` with the following content:
|
||||
|
||||
```ts title="src/modules/brand/index.ts"
|
||||
import { Module } from "@medusajs/framework/utils"
|
||||
import BrandModuleService from "./service"
|
||||
|
||||
export const BRAND_MODULE = "brandModuleService"
|
||||
|
||||
export default Module(BRAND_MODULE, {
|
||||
service: BrandModuleService,
|
||||
})
|
||||
```
|
||||
|
||||
This exposes the module to your application and allows you to resolve the `BrandModuleService`, which is its main service.
|
||||
|
||||
<Note>
|
||||
|
||||
Learn more about modules and services [in this guide](../../../basics/modules/page.mdx).
|
||||
|
||||
</Note>
|
||||
|
||||
---
|
||||
|
||||
## 5. Register Module in Config
|
||||
|
||||
Finally, add the module to Medusa's configurations in `medusa-config.ts`:
|
||||
|
||||
```ts title="medusa-config.ts"
|
||||
module.exports = defineConfig({
|
||||
// ...
|
||||
modules: [
|
||||
{
|
||||
resolve: "./src/modules/brand",
|
||||
}
|
||||
]
|
||||
})
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. Generate and Run Migrations
|
||||
|
||||
To reflect the data model in the database, generate migrations for the `brandModuleService` module and migrate the changes to the database:
|
||||
|
||||
```bash
|
||||
npx medusa db:generate brandModuleService
|
||||
npx medusa db:migrate
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Next Step: Create Brand Workflow
|
||||
|
||||
In the next step, you'll create a workflow whose steps use the Brand Module's main service to create a brand.
|
||||
@@ -0,0 +1,25 @@
|
||||
export const metadata = {
|
||||
title: `${pageNumber} Build Custom Features`,
|
||||
}
|
||||
|
||||
# {metadata.title}
|
||||
|
||||
In this chapter, you'll learn about the concepts you need to build custom features in your Medusa application.
|
||||
|
||||
To add a custom feature to your application, you create:
|
||||
|
||||
1. A module with data models and a main service to manage them.
|
||||
2. A workflow to create, update, and delete records of data models. You implement functionalities in a workflow to benefit from features such as roll-back in case of errors, retry configurations, and more.
|
||||
3. An API route that exposes the workflow's functionality to clients, such as the storefront or admin dashboard.
|
||||
|
||||

|
||||
|
||||
---
|
||||
|
||||
## Next Chapters: Brand Module Example
|
||||
|
||||
In the next chapters, you'll follow an example to:
|
||||
|
||||
1. Add a Brand Module that creates a `Brand` data model and provides data-management features.
|
||||
2. Add a workflow to create a brand.
|
||||
3. Expose an API route that allows admin users to create a brand using the workflow.
|
||||
@@ -0,0 +1,152 @@
|
||||
import { Prerequisites } from "docs-ui"
|
||||
|
||||
export const metadata = {
|
||||
title: `${pageNumber} Define Workflow to Create a Brand`,
|
||||
}
|
||||
|
||||
# {metadata.title}
|
||||
|
||||
<Note title="Example Chapter">
|
||||
|
||||
This chapter covers how to define a workflow that creates a brand as part of the ["Build Custom Features" chapter](../page.mdx).
|
||||
|
||||
</Note>
|
||||
|
||||
## Workflows vs Services: Why use Workflows?
|
||||
|
||||
When manipulating data, use workflows instead of invoking a service's methods directly in your API route or other customizations.
|
||||
|
||||
Workflows eliminate data inconsistency in your application with its compensation mechanism that undoes changes if an error occurs. For example, if a workflow's step creates a brand, it also defines a compensation mechanism to remove the brand if an error occurs.
|
||||
|
||||
<Note>
|
||||
|
||||
Learn more about workflows [in this guide](../../../basics/workflows/page.mdx).
|
||||
|
||||
</Note>
|
||||
|
||||
This is even more useful when you create workflows with many steps, or integrate third-party systems.
|
||||
|
||||
---
|
||||
|
||||
## Create createBrandWorkflow
|
||||
|
||||
<Prerequisites
|
||||
items={[
|
||||
{
|
||||
text: "Brand Module",
|
||||
link: "/customization/custom-features/module"
|
||||
}
|
||||
]}
|
||||
/>
|
||||
|
||||
Create the file `src/workflows/create-brand/index.ts` with the following content:
|
||||
|
||||
```ts
|
||||
import {
|
||||
createWorkflow,
|
||||
WorkflowResponse,
|
||||
} from "@medusajs/framework/workflows-sdk"
|
||||
|
||||
export type CreateBrandInput = {
|
||||
name: string
|
||||
}
|
||||
|
||||
export const createBrandWorkflow = createWorkflow(
|
||||
"create-brand",
|
||||
(input: CreateBrandInput) => {
|
||||
// TODO
|
||||
}
|
||||
)
|
||||
```
|
||||
|
||||
For now, this workflow only defines its input. You'll create its step and use it in the workflow.
|
||||
|
||||
---
|
||||
|
||||
## Create createBrandStep
|
||||
|
||||
Create the file `src/workflows/create-brand/steps/create-brand.ts` with the following content:
|
||||
|
||||
```ts title="src/workflows/create-brand/steps/create-brand.ts" collapsibleLines="1-8" expandButtonLabel="Show Imports"
|
||||
import {
|
||||
createStep,
|
||||
StepResponse,
|
||||
} from "@medusajs/framework/workflows-sdk"
|
||||
import { CreateBrandInput } from ".."
|
||||
import { BRAND_MODULE } from "../../../modules/brand"
|
||||
import BrandModuleService from "../../../modules/brand/service"
|
||||
|
||||
export const createBrandStep = createStep(
|
||||
"create-brand-step",
|
||||
async (input: CreateBrandInput, { container }) => {
|
||||
const brandModuleService: BrandModuleService = container.resolve(
|
||||
BRAND_MODULE
|
||||
)
|
||||
|
||||
const brand = await brandModuleService.createBrands(input)
|
||||
|
||||
return new StepResponse(brand, brand.id)
|
||||
}
|
||||
)
|
||||
```
|
||||
|
||||
This defines a `createBrandStep`. In the step, you resolve the Brand Module's main service and use its generated `createBrands` method, which accepts one or more objects of brands to create.
|
||||
|
||||
The step returns the created brand in the first parameter of the `StepResponse`'s constructor.
|
||||
|
||||
### Add Compensation Function to Step
|
||||
|
||||
A compensation function rolls back changes made by the step if an error occurs in the workflow.
|
||||
|
||||
The second parameter of the `StepResponse`'s constructor is passed to the compensation function.
|
||||
|
||||
To add the compensation function, pass a third parameter to `createStep`:
|
||||
|
||||
```ts title="src/workflows/create-brand/steps/create-brand.ts"
|
||||
export const createBrandStep = createStep(
|
||||
// ...
|
||||
async (id: string, { container }) => {
|
||||
const brandModuleService: BrandModuleService = container.resolve(
|
||||
BRAND_MODULE
|
||||
)
|
||||
|
||||
await brandModuleService.deleteBrands(id)
|
||||
}
|
||||
)
|
||||
```
|
||||
|
||||
You resolve the Brand Module's main service and use its generated `deleteBrands` method to delete the brand created by the step.
|
||||
|
||||
<Note title="Tip">
|
||||
|
||||
The `deleteBrands` method accepts an ID or an array of IDs of brands to delete.
|
||||
|
||||
</Note>
|
||||
|
||||
So, when an error occurs during the workflow, the brand that was created by the step is deleted to maintain data consistency.
|
||||
|
||||
---
|
||||
|
||||
## Add Step to Workflow
|
||||
|
||||
Go back to the workflow at `src/workflows/create-brand/index.ts` and import the step you created:
|
||||
|
||||
```ts
|
||||
import { createBrandStep } from "./steps/create-brand"
|
||||
```
|
||||
|
||||
Then, replace the `TODO` with the following:
|
||||
|
||||
```ts
|
||||
const brand = createBrandStep(input)
|
||||
|
||||
return new WorkflowResponse(brand)
|
||||
```
|
||||
|
||||
You use the `createBrandStep` to create the brand and return it in the workflow's response.
|
||||
|
||||
---
|
||||
|
||||
## Next Step: Create Brand API Route
|
||||
|
||||
In the next step, you'll create an API route that allows admin users to create a brand using this workflow.
|
||||
Reference in New Issue
Block a user