diff --git a/www/apps/book/app/learn/advanced-development/api-routes/parameters/page.mdx b/www/apps/book/app/learn/advanced-development/api-routes/parameters/page.mdx
index 4523156ab6..5587d6f00a 100644
--- a/www/apps/book/app/learn/advanced-development/api-routes/parameters/page.mdx
+++ b/www/apps/book/app/learn/advanced-development/api-routes/parameters/page.mdx
@@ -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).
diff --git a/www/apps/book/app/learn/advanced-development/api-routes/validation/page.mdx b/www/apps/book/app/learn/advanced-development/api-routes/validation/page.mdx
index 8c5669ddd7..ad6177ef33 100644
--- a/www/apps/book/app/learn/advanced-development/api-routes/validation/page.mdx
+++ b/www/apps/book/app/learn/advanced-development/api-routes/validation/page.mdx
@@ -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'
+### 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:
diff --git a/www/apps/book/app/learn/advanced-development/module-links/query/page.mdx b/www/apps/book/app/learn/advanced-development/module-links/query/page.mdx
index cb3a50b319..d1b0b542ba 100644
--- a/www/apps/book/app/learn/advanced-development/module-links/query/page.mdx
+++ b/www/apps/book/app/learn/advanced-development/module-links/query/page.mdx
@@ -8,12 +8,6 @@ export const metadata = {
In this chapter, you’ll learn about the Query utility and how to use it to fetch data from modules.
-
-
-Query is in development and is subject to change in future releases.
-
-
-
## What is Query?
Query fetches data across modules. It’s 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.
+
+
+
+Learn more about [specifing fields and relations](!api!/store#select-fields-and-relations) and [pagination](!api!/store#pagination) in the API reference.
+
+
diff --git a/www/apps/book/generated/edit-dates.mjs b/www/apps/book/generated/edit-dates.mjs
index 1fb70dd2e8..d8f050779a 100644
--- a/www/apps/book/generated/edit-dates.mjs
+++ b/www/apps/book/generated/edit-dates.mjs
@@ -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",