docs: add documentation on validateAndTransformQuery (#10060)

* docs: add documentation on validateAndTransformQuery

* fix vale error
This commit is contained in:
Shahed Nasser
2024-11-13 10:59:46 +02:00
committed by GitHub
parent 1d87459951
commit 690c352993
4 changed files with 281 additions and 27 deletions

View File

@@ -95,6 +95,12 @@ export const GET = async (
The value of `req.query.name` is the value passed in `?name=John`, for example.
### Validate Query Parameters
You can apply validation rules on received query parameters to ensure they match specified rules and types.
Learn more in [this documentation](../validation/page.mdx#how-to-validate-request-query-paramters).
---
## Request Body Parameters
@@ -153,3 +159,9 @@ This returns the following JSON object:
"message": "[POST] Hello John!"
}
```
### Validate Body Parameters
You can apply validation rules on received body parameters to ensure they match specified rules and types.
Learn more in [this documentation](../validation/page.mdx#how-to-validate-request-body).

View File

@@ -1,24 +1,33 @@
export const metadata = {
title: `${pageNumber} Request Body Parameter Validation`,
title: `${pageNumber} Request Body and Query Parameter Validation`,
}
# {metadata.title}
In this chapter, you'll learn how to validate request body parameters in your custom API route.
In this chapter, you'll learn how to validate request body and query parameters in your custom API route.
## Example Scenario
## Request Validation
Consider you're creating a `POST` API route at `/custom`. It accepts two parameters `a` and `b` that are required numbers, and returns their sum.
The next steps explain how to add validation to this API route, as an example.
Medusa provides two middlewares to validate the request body and query paramters of incoming requests to your custom API routes:
- `validateAndTransformBody` to validate the request's body parameters against a schema.
- `validateAndTransformQuery` to validate the request's query parameters against a schema.
Both middlewares accept a [Zod](https://zod.dev/) schema as a parameter, which gives you flexibility in how you define your validation schema with complex rules.
The next steps explain how to add request body and query parameter validation to the API route mentioned earlier.
---
## Step 1: Create Zod Schema
## How to Validate Request Body
Medusa uses [Zod](https://zod.dev/) to validate the body parameters of an incoming request.
### Step 1: Create Validation Schema
To use Zod to validate your custom schemas, create a `validators.ts` file in any `src/api` subfolder. This file holds Zod schemas for each of your API routes.
Medusa uses [Zod](https://zod.dev/) to create validation schemas. These schemas are then used to validate incoming request bodies or query parameters.
To create a validation schema with Zod, create a `validators.ts` file in any `src/api` subfolder. This file holds Zod schemas for each of your API routes.
For example, create the file `src/api/custom/validators.ts` with the following content:
@@ -37,11 +46,9 @@ The `PostStoreCustomSchema` variable is a Zod schema that indicates the request
2. It has a property `a` that is a required number.
3. It has a property `b` that is a required number.
---
### Step 2: Add Request Body Validation Middleware
## Step 2: Add Validation Middleware
To use this schema for validating the body parameters of requests to `/custom`, use the `validateAndTransformBody` middleware provided by `@medusajs/framework/utils`. It accepts the Zod schema as a parameter.
To use this schema for validating the body parameters of requests to `/custom`, use the `validateAndTransformBody` middleware provided by `@medusajs/framework/http`. It accepts the Zod schema as a parameter.
For example, create the file `src/api/middlewares.ts` with the following content:
@@ -49,7 +56,7 @@ For example, create the file `src/api/middlewares.ts` with the following content
import { defineMiddlewares } from "@medusajs/medusa"
import {
validateAndTransformBody,
} from "@medusajs/framework/utils"
} from "@medusajs/framework/http"
import { PostStoreCustomSchema } from "./custom/validators"
export default defineMiddlewares({
@@ -67,15 +74,13 @@ export default defineMiddlewares({
This applies the `validateAndTransformBody` middleware on `POST` requests to `/custom`. It uses the `PostStoreCustomSchema` as the validation schema.
### How the Validation Works
#### How the Validation Works
If a request's body parameters don't pass the validation, the `validateAndTransformBody` middleware throws an error indicating the validation errors.
If a request's body parameters are validated successfully, the middleware sets the validated body parameters in the `validatedBody` property of `MedusaRequest`.
---
## Step 3: Use Validated Body in API Route
### Step 3: Use Validated Body in API Route
In your API route, consume the validated body using the `validatedBody` property of `MedusaRequest`.
@@ -113,11 +118,131 @@ To pass the request body's type as a type parameter to `MedusaRequest`, use Zod'
</Note>
### Test it Out
To test out the validation, send a `POST` request to `/custom` passing `a` and `b` body parameters. You can try sending incorrect request body parameters to test out the validation.
For example, if you omit the `a` parameter, you'll receive a `400` response code with the following response data:
```json
{
"type": "invalid_data",
"message": "Invalid request: Field 'a' is required"
}
```
---
## Test it Out
## How to Validate Request Query Paramters
To test out the validation, send a `POST` request to `/custom`. You can try sending incorrect request body parameters.
The steps to validate the request query parameters are the similar to that of [validating the body](#how-to-validate-request-body).
### Step 1: Create Validation Schema
The first step is to create a schema with Zod with the rules of the accepted query parameters.
Consider that the API route accepts two query parameters `a` and `b` that are numbers, similar to the previous section.
Create the file `src/api/custom/validators.ts` with the following content:
```ts title="src/api/custom/validators.ts"
import { z } from "zod"
export const PostStoreCustomSchema = z.object({
a: z.preprocess(
(val) => {
if (val && typeof val === "string") {
return parseInt(val)
}
return val
},
z
.number()
),
b: z.preprocess(
(val) => {
if (val && typeof val === "string") {
return parseInt(val)
}
return val
},
z
.number()
),
})
```
Since a query parameter's type is originally a string or array of strings, you have to use Zod's `preprocess` method to validate other query types, such as numbers.
For both `a` and `b`, you transform the query parameter's value to an integer first if it's a string, then, you check that the resulting value is a number.
### Step 2: Add Request Query Validation Middleware
Next, you'll use the schema to validate incoming requests' query parameters to the `/custom` API route.
Add the `validateAndTransformQuery` middleware to the API route in the file `src/api/middlewares.ts`:
```ts title="src/api/middlewares.ts"
import { defineMiddlewares } from "@medusajs/medusa"
import {
validateAndTransformQuery,
} from "@medusajs/framework/http"
import { PostStoreCustomSchema } from "./custom/validators"
export default defineMiddlewares({
routes: [
{
matcher: "/custom",
method: "POST",
middlewares: [
validateAndTransformQuery(
PostStoreCustomSchema,
{}
),
],
},
],
})
```
The `validateAndTransformQuery` accepts two parameters:
- The first one is the Zod schema to validate the query parameters against.
- The second one is an object of options for retrieving data using Query, which you can learn more about in [this chapter](../../module-links/query/page.mdx).
#### How the Validation Works
If a request's query parameters don't pass the validation, the `validateAndTransformQuery` middleware throws an error indicating the validation errors.
If a request's query parameters are validated successfully, the middleware sets the validated query parameters in the `validatedQuery` property of `MedusaRequest`.
### Step 3: Use Validated Query in API Route
Finally, use the validated query in the API route. The `MedusaRequest` parameter has a `validatedQuery` parameter that you can use to access the validated parameters.
For example, create the file `src/api/custom/route.ts` with the following content:
```ts title="src/api/custom/route.ts"
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
export const GET = async (
req: MedusaRequest,
res: MedusaResponse
) => {
const a = req.validatedQuery.a as number
const b = req.validatedQuery.b as number
res.json({
sum: a + b
})
}
```
In the API route, you use the `validatedQuery` property of `MedusaRequest` to access the values of the `a` and `b` properties as numbers, then return in the response their sum.
### Test it Out
To test out the validation, send a `POST` request to `/custom` with `a` and `b` query parameters. You can try sending incorrect query parameters to see how the validation works.
For example, if you omit the `a` parameter, you'll receive a `400` response code with the following response data:

View File

@@ -8,12 +8,6 @@ export const metadata = {
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.
@@ -227,3 +221,126 @@ When you provide the pagination fields, the `query.graph` method's returned obje
description: "The total number of records."
}
]} sectionTitle="Apply Pagination" />
---
## Request Query Configurations
For API routes that retrieve a single or list of resources, Medusa provides a `validateAndTransformQuery` middleware that:
- Validates accepted query parameters, as explained in [this documentation](../../api-routes/validation/page.mdx).
- Parses configurations that are received as query parameters to be passed to Query.
Using this middleware allows you to have default configurations for retrieved fields and relations or pagination, while allowing clients to customize them per request.
### Step 1: Add Middleware
The first step is to use the `validateAndTransformQuery` middleware on the `GET` route. You add the middleware in `src/api/middlewares.ts`:
```ts title="src/api/middlewares.ts"
import { defineMiddlewares } from "@medusajs/medusa"
import {
validateAndTransformQuery,
} from "@medusajs/framework/http"
import { createFindParams } from "@medusajs/medusa/api/utils/validators"
export const GetCustomSchema = createFindParams()
export default defineMiddlewares({
routes: [
{
matcher: "/customs",
method: "GET",
middlewares: [
validateAndTransformQuery(
GetCustomSchema,
{
defaults: [
"id",
"name",
"products.*"
],
isList: true
}
),
],
},
],
})
```
The `validateAndTransformQuery` accepts two parameters:
1. A Zod validation schema for the query parameters, which you can learn more about in the [API Route Validation documentation](../../api-routes/validation/page.mdx). Medusa has a `createFindParams` utility that generates a Zod schema that accepts four query parameters:
1. `fields`: The fields and relations to retrieve in the returned resources.
2. `offset`: The number of items to skip before retrieving the returned items.
3. `limit`: The maximum number of items to return.
4. `order`: The fields to order the returned items by in ascending or descending order.
2. A Query configuration object. It accepts the following properties:
1. `defaults`: An array of default fields and relations to retrieve in each resource.
2. `isList`: A boolean indicating whether a list of items are returned in the response.
3. `allowed`: An array of fields and relations allowed to be passed in the `fields` query parameter.
4. `defaultLimit`: A number indicating the default limit to use if no limit is provided. By default, it's `50`.
### Step 2: Use Configurations in API Route
After applying this middleware, your API route now accepts the `fields`, `offset`, `limit`, and `order` query parameters mentioned above.
The middleware transforms these parameters to configurations that you can pass to Query in your API route handler. These configurations are stored in the `remoteQueryConfig` parameter of the `MedusaRequest` object.
For example, Create the file `src/api/customs/route.ts` with the following content:
export const queryConfigHighlights = [
["17", "req.remoteQueryConfig", "Pass the parsed request Query configurations to the Query graph execution."]
]
```ts title="src/api/customs/route.ts"
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",
...req.remoteQueryConfig
})
res.json({ my_customs: myCustoms })
}
```
This adds a `GET` API route at `/customs`, which is the API route you added the middleware for.
In the API route, you pass `req.remoteQueryConfig` to `query.graph`. `remoteQueryConfig` has properties like `fields` and `pagination` to configure the query based on the default values you specified in the middleware, and the query parameters passed in the request.
### Test it Out
To test it out, start your Medusa application and send a `GET` request to the `/customs` API route. A list of records are retrieved with the specified fields in the middleware.
```json title="Returned Data"
{
"my_customs": [
{
"id": "123",
"name": "test"
}
]
}
```
Try passing one of the Query configuration parameters, like `fields` or `limit`, and you'll see its impact on the returned result.
<Note>
Learn more about [specifing fields and relations](!api!/store#select-fields-and-relations) and [pagination](!api!/store#pagination) in the API reference.
</Note>

View File

@@ -52,7 +52,7 @@ export const generatedEditDates = {
"app/learn/advanced-development/modules/module-links/page.mdx": "2024-09-30T08:43:53.126Z",
"app/learn/advanced-development/data-models/searchable-property/page.mdx": "2024-09-30T08:43:53.125Z",
"app/learn/advanced-development/scheduled-jobs/execution-number/page.mdx": "2024-07-02T09:41:15+00:00",
"app/learn/advanced-development/api-routes/parameters/page.mdx": "2024-09-11T10:44:13.491Z",
"app/learn/advanced-development/api-routes/parameters/page.mdx": "2024-11-12T13:35:09.393Z",
"app/learn/advanced-development/api-routes/http-methods/page.mdx": "2024-09-11T10:43:33.169Z",
"app/learn/advanced-development/admin/tips/page.mdx": "2024-10-07T12:50:36.335Z",
"app/learn/advanced-development/api-routes/cors/page.mdx": "2024-09-30T08:43:53.121Z",
@@ -72,7 +72,7 @@ export const generatedEditDates = {
"app/learn/advanced-development/modules/service-constraints/page.mdx": "2024-09-30T08:43:53.127Z",
"app/learn/advanced-development/api-routes/page.mdx": "2024-09-04T09:36:33.961Z",
"app/learn/advanced-development/api-routes/responses/page.mdx": "2024-09-11T10:44:37.016Z",
"app/learn/advanced-development/api-routes/validation/page.mdx": "2024-09-11T10:46:31.476Z",
"app/learn/advanced-development/api-routes/validation/page.mdx": "2024-11-12T13:32:32.484Z",
"app/learn/advanced-development/api-routes/errors/page.mdx": "2024-09-30T08:43:53.121Z",
"app/learn/advanced-development/admin/constraints/page.mdx": "2024-09-10T11:39:51.165Z",
"app/learn/debugging-and-testing/testing-tools/modules-tests/module-example/page.mdx": "2024-10-16T08:50:03.061Z",
@@ -80,7 +80,7 @@ export const generatedEditDates = {
"app/learn/advanced-development/module-links/custom-columns/page.mdx": "2024-09-16T15:51:33.570Z",
"app/learn/advanced-development/module-links/directions/page.mdx": "2024-09-16T15:37:51.441Z",
"app/learn/advanced-development/module-links/page.mdx": "2024-09-16T15:36:48.190Z",
"app/learn/advanced-development/module-links/query/page.mdx": "2024-09-16T12:42:27.579Z",
"app/learn/advanced-development/module-links/query/page.mdx": "2024-11-12T15:40:24.411Z",
"app/learn/advanced-development/module-links/remote-link/page.mdx": "2024-09-16T12:42:27.581Z",
"app/learn/advanced-development/modules/db-operations/page.mdx": "2024-09-16T14:38:29.150Z",
"app/learn/advanced-development/modules/multiple-services/page.mdx": "2024-09-16T14:41:32.975Z",