docs: revise brand API route (#10352)
* docs: revise brand API route * address feedback * add directory images * change sidebar title * address comments
This commit is contained in:
@@ -1,16 +1,16 @@
|
||||
import { Prerequisites } from "docs-ui"
|
||||
|
||||
export const metadata = {
|
||||
title: `${pageNumber} Create Brand API Route`,
|
||||
title: `${pageNumber} Guide: Create Brand API Route`,
|
||||
}
|
||||
|
||||
# {metadata.title}
|
||||
|
||||
<Note title="Example Chapter">
|
||||
In the previous two chapters, you created a [Brand Module](../module/page.mdx) that added the concepts of brands to your application, then created a [workflow to create a brand](../workflow/page.mdx). In this chapter, you'll expose an API route that allows admin users to create a brand using the workflow from the previous 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).
|
||||
An API Route is an endpoint that acts as an entry point for other clients to interact with your Medusa customizations, such as the admin dashboard, storefronts, or third-party systems.
|
||||
|
||||
</Note>
|
||||
The Medusa core application provides a set of [admin](!api!/admin) and [store](!api!/store) API routes out-of-the-box. You can also create custom API routes to expose your custom functionalities.
|
||||
|
||||
<Prerequisites
|
||||
items={[
|
||||
@@ -21,34 +21,9 @@ This chapter covers how to define an API route that creates a brand as the last
|
||||
]}
|
||||
/>
|
||||
|
||||
Create the file `src/api/admin/brands/route.ts` with the following content:
|
||||
## 1. Create the API Route
|
||||
|
||||
```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.
|
||||
You create an API route in a `route.{ts,js}` file under a sub-directory of the `src/api` directory. The file exports API Route handler functions for at least one HTTP method (`GET`, `POST`, `DELETE`, etc…).
|
||||
|
||||
<Note>
|
||||
|
||||
@@ -56,11 +31,151 @@ Learn more about API routes [in this guide](../../../basics/api-routes/page.mdx)
|
||||
|
||||
</Note>
|
||||
|
||||
The route's path is the path of `route.{ts,js}` relative to `src/api`. So, to create the API route at `/admin/brands`, create the file `src/api/admin/brands/route.ts` with the following content:
|
||||
|
||||

|
||||
|
||||
```ts title="src/api/admin/brands/route.ts"
|
||||
import {
|
||||
MedusaRequest,
|
||||
MedusaResponse,
|
||||
} from "@medusajs/framework/http"
|
||||
import {
|
||||
createBrandWorkflow,
|
||||
} from "../../../workflows/create-brand"
|
||||
|
||||
type PostAdminCreateBrandType = {
|
||||
name: string
|
||||
}
|
||||
|
||||
export const POST = async (
|
||||
req: MedusaRequest<PostAdminCreateBrandType>,
|
||||
res: MedusaResponse
|
||||
) => {
|
||||
const { result } = await createBrandWorkflow(req.scope)
|
||||
.run({
|
||||
input: req.validatedBody,
|
||||
})
|
||||
|
||||
res.json({ brand: result })
|
||||
}
|
||||
```
|
||||
|
||||
You export a route handler function with its name (`POST`) being the HTTP method of the API route you're exposing.
|
||||
|
||||
The function receives two parameters: a `MedusaRequest` object to access request details, and `MedusaResponse` object to return or manipulate the response. The `MedusaRequest` object's `scope` property is the [Medusa container](../../../basics/medusa-container/page.mdx) that holds framework tools and custom and core modules' services.
|
||||
|
||||
<Note title="Tip">
|
||||
|
||||
`MedusaRequest` accepts the request body's type as a type argument.
|
||||
|
||||
</Note>
|
||||
|
||||
In the API route's handler, you execute the `createBrandWorkflow` by invoking it and passing the Medusa container `req.scope` as a parameter, then invoking its `run` method. You pass the workflow's input in the `input` property of the `run` method's parameter. You pass the request body's parameters using the `validatedBody` property of `MedusaRequest`.
|
||||
|
||||
You return a JSON response with the created brand using the `res.json` method.
|
||||
|
||||
---
|
||||
|
||||
## 2. Create Validation Schema
|
||||
|
||||
The API route you created accepts the brand's name in the request body. So, you'll create a schema used to validate incoming request body parameters.
|
||||
|
||||
Medusa uses [Zod](https://zod.dev/) to create validation schemas. These schemas are then used to validate incoming request bodies or query parameters.
|
||||
|
||||
<Note>
|
||||
|
||||
Learn more about API route validation in [this chapter](../../../advanced-development/api-routes/validation/page.mdx).
|
||||
|
||||
</Note>
|
||||
|
||||
You create a validation schema in a TypeScript or JavaScript file under a sub-directory of the `src/api` directory. So, create the file `src/api/admin/brands/validators.ts` with the following content:
|
||||
|
||||

|
||||
|
||||
```ts title="src/api/admin/brands/validators.ts"
|
||||
import { z } from "zod"
|
||||
|
||||
export const PostAdminCreateBrand = z.object({
|
||||
name: z.string()
|
||||
})
|
||||
```
|
||||
|
||||
You export a validation schema that expects in the request body an object having a `name` property whose value is a string.
|
||||
|
||||
You can then replace `PostAdminCreateBrandType` in `src/api/admin/brands/route.ts` with the following:
|
||||
|
||||
```ts title="src/api/admin/brands/route.ts"
|
||||
// ...
|
||||
import { z } from "zod"
|
||||
import { PostAdminCreateBrand } from "./validators"
|
||||
|
||||
type PostAdminCreateBrandType = z.infer<typeof PostAdminCreateBrand>
|
||||
|
||||
// ...
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. Add Validation Middleware
|
||||
|
||||
A middleware is a function executed before the route handler when a request is sent to an API Route. It's useful to guard API routes, parse custom request body types, and apply validation on an API route.
|
||||
|
||||
<Note>
|
||||
|
||||
Learn more about middlewares in [this chapter](../../../advanced-development/api-routes/middlewares/page.mdx).
|
||||
|
||||
</Note>
|
||||
|
||||
Medusa provides a `validateAndTransformBody` middleware that accepts a Zod validation schema and returns a response error if a request is sent with body parameters that don't satisfy the validation schema.
|
||||
|
||||
Middlewares are defined in the special file `src/api/middlewares.ts`. So, to add the validation middleware on the API route you created in the previous step, create the file `src/api/middlewares.ts` with the following content:
|
||||
|
||||

|
||||
|
||||
```ts title="src/api/middlewares.ts"
|
||||
import {
|
||||
defineMiddlewares,
|
||||
validateAndTransformBody,
|
||||
} from "@medusajs/framework/http"
|
||||
import { PostAdminCreateBrand } from "./admin/brands/validators"
|
||||
|
||||
export default defineMiddlewares({
|
||||
routes: [
|
||||
{
|
||||
matcher: "/admin/brands",
|
||||
method: "POST",
|
||||
middlewares: [
|
||||
validateAndTransformBody(PostAdminCreateBrand),
|
||||
],
|
||||
},
|
||||
],
|
||||
})
|
||||
```
|
||||
|
||||
You define the middlewares using the `defineMiddlewares` function and export its returned value. The function accepts an object having a `routes` property, which is an array of middleware objects.
|
||||
|
||||
In the middleware object, you define three properties:
|
||||
|
||||
- `matcher`: a string or regular expression indicating the API route path to apply the middleware on. You pass the create brand's route `/admin/brand`.
|
||||
- `method`: The HTTP method to restrict the middleware to, which is `POST`.
|
||||
- `middlewares`: An array of middlewares to apply on the route. You pass the `validateAndTransformBody` middleware, passing it the Zod schema you created earlier.
|
||||
|
||||
The Medusa application will now validate the body parameters of `POST` requests sent to `/admin/brands` to ensure they match the Zod validation schema. If not, an error is returned in the response specifying the issues to fix in the request body.
|
||||
|
||||
---
|
||||
|
||||
## 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:
|
||||
To test out the API route, start the Medusa application with the following command:
|
||||
|
||||
```bash npm2yarn
|
||||
npm run dev
|
||||
```
|
||||
|
||||
Since the `/admin/brands` API route has a `/admin` prefix, it's only accessible by authenticated admin users.
|
||||
|
||||
So, to retrieve an authenticated token of your admin user, send a `POST` request to the `/auth/user/emailpass` API Route:
|
||||
|
||||
```bash
|
||||
curl -X POST 'http://localhost:9000/auth/user/emailpass' \
|
||||
@@ -71,7 +186,13 @@ curl -X POST 'http://localhost:9000/auth/user/emailpass' \
|
||||
}'
|
||||
```
|
||||
|
||||
Make sure to replace the email and password with your user's credentials.
|
||||
Make sure to replace the email and password with your admin user's credentials.
|
||||
|
||||
<Note title="Tip">
|
||||
|
||||
Don't have an admin user? Refer to [this guide](../../../installation/page.mdx#create-medusa-admin-user).
|
||||
|
||||
</Note>
|
||||
|
||||
Then, send a `POST` request to `/admin/brands`, passing the token received from the previous request in the `Authorization` header:
|
||||
|
||||
@@ -101,14 +222,16 @@ This returns the created brand in the response:
|
||||
|
||||
## Summary
|
||||
|
||||
By following the previous example chapters, you implemented a custom feature that allows admin users to create a brand by:
|
||||
By following the previous example chapters, you implemented a custom feature that allows admin users to create a brand. You did that 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.
|
||||
1. Creating a module that defines and manages a `brand` table in the database.
|
||||
2. Creating a workflow that uses the module's 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
|
||||
## Next Steps: Associate Brand with Product
|
||||
|
||||
In the next chapters, you'll learn how to extend data models and associate the brand with a product.
|
||||
Now that you have brands in your Medusa application, you want to associate a brand with a product, which is defined in the [Product Module](!resources!/commerce-modules/product).
|
||||
|
||||
In the next chapters, you'll learn how to build associations between data models defined in different modules.
|
||||
|
||||
@@ -8,7 +8,7 @@ In this chapter, you'll build a Brand Module that adds a `brand` table to the da
|
||||
|
||||
A module is a reusable package of functionalities related to a single domain or integration. Medusa comes with multiple pre-built modules for core commerce needs, such as the [Cart Module](!resources!/commerce-modules/cart) that holds the data models and business logic for cart operations.
|
||||
|
||||
You create in a module new tables in the database, and expose a class that provides data-management methods on those tables. In the next chapters, you'll see how you use the module's functionalities to expose commerce features.
|
||||
In a module, you create data models and business logic to manage them. In the next chapters, you'll see how you use the module to build commerce features.
|
||||
|
||||
<Note>
|
||||
|
||||
@@ -20,6 +20,8 @@ Learn more about modules in [this chapter](../../../basics/modules/page.mdx).
|
||||
|
||||
Modules are created in a sub-directory of `src/modules`. So, start by creating the directory `src/modules/brand` that will hold the Brand Module's files.
|
||||
|
||||

|
||||
|
||||
---
|
||||
|
||||
## 2. Create Data Model
|
||||
@@ -34,6 +36,8 @@ Learn more about data models in [this chapter](../../../basics/modules/page.mdx#
|
||||
|
||||
You create a data model in a TypeScript or JavaScript file under the `models` directory of a module. So, 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"
|
||||
|
||||
@@ -72,6 +76,8 @@ Learn more about services in [this chapter](../../../basics/modules/page.mdx#2-c
|
||||
|
||||
You define a service in a `service.ts` or `service.js` file at the root of your module's directory. So, 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."]
|
||||
]
|
||||
@@ -109,6 +115,8 @@ A module must export a definition that tells Medusa the name of the module and i
|
||||
|
||||
So, to export the Brand 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"
|
||||
|
||||
@@ -8,10 +8,10 @@ In the upcoming chapters, you'll follow step-by-step guides to build custom feat
|
||||
|
||||
By following these guides, you'll add brands to the Medusa application that you can associate with products.
|
||||
|
||||
To build a custom feature in Medusa, you need three main ingredients:
|
||||
To build a custom feature in Medusa, you need three main tools:
|
||||
|
||||
- [Module](../../basics/modules/page.mdx): a re-usable package that defines commerce functionalities for a single domain. It defines new tables to add to the database, and a class of methods to manage these tables.
|
||||
- [Workflow](../../basics/workflows/page.mdx): a special function that performs a task in a series of steps with advanced features like roll-back mechanism and retry configurations. The steps of a workflow use functionalities implemented by modules.
|
||||
- [Module](../../basics/modules/page.mdx): a package with commerce logic for a single domain. It defines new tables to add to the database, and a class of methods to manage these tables.
|
||||
- [Workflow](../../basics/workflows/page.mdx): a tool to perform an operation comprising multiple steps with built-in rollback and retry mechanisms.
|
||||
- [API route](../../basics/api-routes/page.mdx): a REST endpoint that exposes commerce features to clients, such as the admin dashboard or a storefront. The API route executes a workflow that implements the commerce feature using modules.
|
||||
|
||||

|
||||
|
||||
@@ -6,9 +6,9 @@ export const metadata = {
|
||||
|
||||
# {metadata.title}
|
||||
|
||||
This chapter is a follow-up to the [previous one](../module/page.mdx) where you created a Brand Module. In this chapter, you'll create a workflow that creates a brand.
|
||||
This chapter builds on the work from the [previous chapter](../module/page.mdx) where you created a Brand Module.
|
||||
|
||||
You implement commerce features within workflows. A workflow is a series of queries and actions, called steps, that complete a task. You construct a workflow similar to a regular function, but it's a special function that allows you to define roll-back logic, retry configurations, and more advanced features.
|
||||
After adding custom brands to your application, you build commerce features around them using workflows. A workflow is a series of queries and actions, called steps, that complete a task spanning across modules. You construct a workflow similar to a regular function, but it's a special function that allows you to define roll-back logic, retry configurations, and more advanced features.
|
||||
|
||||
The workflow you'll create in this chapter will use the Brand Module's service to implement the feature of creating a brand. In the [next chapter](../api-route/page.mdx), you'll expose an API route that allows admin users to create a brand, and you'll use this workflow in the route's implementation.
|
||||
|
||||
@@ -33,9 +33,11 @@ Learn more about workflows in [this chapter](../../../basics/workflows/page.mdx)
|
||||
|
||||
A workflow consists of a series of steps, each step created in a TypeScript or JavaScript file under the `src/workflows` directory. A step is defined using the `createStep` utility function imported from `@medusajs/framework/workflows-sdk`.
|
||||
|
||||
The workflow you're creating in this guide has one step to create the brand. So, create the file `src/workflows/create-brand/steps/create-brand.ts` with the following content:
|
||||
The workflow you're creating in this guide has one step to create the brand. So, create the file `src/workflows/create-brand.ts` with the following content:
|
||||
|
||||
```ts title="src/workflows/create-brand/steps/create-brand.ts"
|
||||

|
||||
|
||||
```ts title="src/workflows/create-brand.ts"
|
||||
import {
|
||||
createStep,
|
||||
StepResponse,
|
||||
@@ -89,7 +91,7 @@ Learn more about the compensation function in [this chapter](../../../advanced-d
|
||||
|
||||
To add a compensation function to the `createBrandStep`, pass it as a third parameter to `createStep`:
|
||||
|
||||
```ts title="src/workflows/create-brand/steps/create-brand.ts"
|
||||
```ts title="src/workflows/create-brand.ts"
|
||||
export const createBrandStep = createStep(
|
||||
// ...
|
||||
async (id: string, { container }) => {
|
||||
@@ -120,22 +122,25 @@ So, if an error occurs during the workflow's execution, the brand that was creat
|
||||
|
||||
You can now create the workflow that runs the `createBrandStep`. A workflow is created in a TypeScript or JavaScript file under the `src/workflows` directory. In the file, you use the `createWorkflow` function imported from `@medusajs/framework/workflows-sdk` to create the workflow.
|
||||
|
||||
So, create the file `src/workflows/create-brand/index.ts` with the following content:
|
||||
Add the following content in the same `src/workflows/create-brand.ts` file:
|
||||
|
||||
```ts
|
||||
// other imports...
|
||||
import {
|
||||
// ...
|
||||
createWorkflow,
|
||||
WorkflowResponse,
|
||||
} from "@medusajs/framework/workflows-sdk"
|
||||
import { createBrandStep } from "./steps/create-brand"
|
||||
|
||||
type CreateBrandInput = {
|
||||
// ...
|
||||
|
||||
type CreateBrandWorkflowInput = {
|
||||
name: string
|
||||
}
|
||||
|
||||
export const createBrandWorkflow = createWorkflow(
|
||||
"create-brand",
|
||||
(input: CreateBrandInput) => {
|
||||
(input: CreateBrandWorkflowInput) => {
|
||||
const brand = createBrandStep(input)
|
||||
|
||||
return new WorkflowResponse(brand)
|
||||
|
||||
@@ -89,7 +89,7 @@ export const generatedEditDates = {
|
||||
"app/learn/advanced-development/api-routes/additional-data/page.mdx": "2024-09-30T08:43:53.120Z",
|
||||
"app/learn/advanced-development/workflows/page.mdx": "2024-09-18T08:00:57.364Z",
|
||||
"app/learn/advanced-development/workflows/variable-manipulation/page.mdx": "2024-11-14T16:11:24.538Z",
|
||||
"app/learn/customization/custom-features/api-route/page.mdx": "2024-09-12T12:42:34.201Z",
|
||||
"app/learn/customization/custom-features/api-route/page.mdx": "2024-11-28T13:12:10.521Z",
|
||||
"app/learn/customization/custom-features/module/page.mdx": "2024-11-28T09:25:29.098Z",
|
||||
"app/learn/customization/custom-features/workflow/page.mdx": "2024-11-28T10:47:28.084Z",
|
||||
"app/learn/customization/extend-models/create-links/page.mdx": "2024-09-30T08:43:53.133Z",
|
||||
|
||||
@@ -108,7 +108,7 @@ export const sidebar = numberSidebarItems(
|
||||
},
|
||||
{
|
||||
type: "link",
|
||||
title: "Create Brand API Route",
|
||||
title: "Brand API Route",
|
||||
path: "/learn/customization/custom-features/api-route",
|
||||
},
|
||||
],
|
||||
|
||||
Reference in New Issue
Block a user