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:
Shahed Nasser
2024-12-04 12:19:42 +02:00
committed by GitHub
parent a5c8cc992c
commit 563b725ed6
6 changed files with 189 additions and 53 deletions
@@ -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:
![Directory structure of the Medusa application after adding the route](https://res.cloudinary.com/dza7lstvk/image/upload/v1732869882/Medusa%20Book/brand-route-dir-overview-2_hjqlnf.jpg)
```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:
![Directory structure of Medusa application after adding validators file](https://res.cloudinary.com/dza7lstvk/image/upload/v1732869806/Medusa%20Book/brand-route-dir-overview-1_yfyjss.jpg)
```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:
![Directory structure of the Medusa application after adding the middleware](https://res.cloudinary.com/dza7lstvk/image/upload/v1732869977/Medusa%20Book/brand-route-dir-overview-3_kcx511.jpg)
```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.